mirror of
https://github.com/claunia/flac.git
synced 2025-12-16 18:54:26 +00:00
Second patch from X-Fixer: file info dialog, tag editor, tag formatting in plugin config
This commit is contained in:
@@ -23,5 +23,7 @@ EXTRA_DIST = \
|
||||
config.c \
|
||||
in_flac.c \
|
||||
in_flac.dsp \
|
||||
infobox.c \
|
||||
infobox.h \
|
||||
resource.h \
|
||||
resource.rc
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
#include <windows.h>
|
||||
#include <commctrl.h>
|
||||
#include <stdio.h>
|
||||
#include "winamp2/in2.h"
|
||||
#include "winamp2/frontend.h"
|
||||
#include "config.h"
|
||||
#include "tagz.h"
|
||||
#include "resource.h"
|
||||
|
||||
|
||||
static char buffer[256];
|
||||
|
||||
//
|
||||
// read/write
|
||||
// Read/write config
|
||||
//
|
||||
|
||||
#define RI(x, def) (x = GetPrivateProfileInt("FLAC", #x, def, ini_name))
|
||||
#define WI(x) WritePrivateProfileString("FLAC", #x, itoa(x, buffer, 10), ini_name)
|
||||
#define RS(x, n, def) GetPrivateProfileString("FLAC", #x, def, x, n, ini_name)
|
||||
#define WS(x) WritePrivateProfileString("FLAC", #x, x, ini_name)
|
||||
|
||||
static const char default_format[] = "[%artist% - ]$if2(%title%,%filename%)";
|
||||
|
||||
void ReadConfig()
|
||||
{
|
||||
RS(flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format), default_format);
|
||||
|
||||
RI(flac_cfg.output.replaygain.enable, 1);
|
||||
RI(flac_cfg.output.replaygain.album_mode, 0);
|
||||
RI(flac_cfg.output.replaygain.hard_limit, 0);
|
||||
@@ -31,6 +35,8 @@ void ReadConfig()
|
||||
|
||||
void WriteConfig()
|
||||
{
|
||||
WS(flac_cfg.title.tag_format);
|
||||
|
||||
WI(flac_cfg.output.replaygain.enable);
|
||||
WI(flac_cfg.output.replaygain.album_mode);
|
||||
WI(flac_cfg.output.replaygain.hard_limit);
|
||||
@@ -42,15 +48,48 @@ void WriteConfig()
|
||||
}
|
||||
|
||||
//
|
||||
// dialog
|
||||
// Dialog
|
||||
//
|
||||
|
||||
#define PREAMP_RANGE 24
|
||||
|
||||
#define Check(x,y) CheckDlgButton(hwnd, x, y ? BST_CHECKED : BST_UNCHECKED)
|
||||
#define GetCheck(x) (IsDlgButtonChecked(hwnd, x)==BST_CHECKED)
|
||||
#define GetSel(x) SendDlgItemMessage(hwnd, x, CB_GETCURSEL, 0, 0)
|
||||
#define GetPos(x) SendDlgItemMessage(hwnd, x, TBM_GETPOS, 0, 0)
|
||||
#define Enable(x,y) EnableWindow(GetDlgItem(hwnd, x), y)
|
||||
|
||||
#define PREAMP_RANGE 24
|
||||
|
||||
static INT_PTR CALLBACK GeneralProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
// init
|
||||
case WM_INITDIALOG:
|
||||
SetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format);
|
||||
return TRUE;
|
||||
// commands
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
// ok
|
||||
case IDOK:
|
||||
GetDlgItemText(hwnd, IDC_TITLE, flac_cfg.title.tag_format, sizeof(flac_cfg.title.tag_format));
|
||||
break;
|
||||
// reset
|
||||
case IDC_RESET:
|
||||
SetDlgItemText(hwnd, IDC_TITLE, default_format);
|
||||
break;
|
||||
// help
|
||||
case IDC_TAGZ_HELP:
|
||||
MessageBox(hwnd, tagz_manual, "Help", 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void UpdatePreamp(HWND hwnd, HWND hamp)
|
||||
@@ -60,7 +99,22 @@ static void UpdatePreamp(HWND hwnd, HWND hamp)
|
||||
SetDlgItemText(hwnd, IDC_PA, buffer);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
static void UpdateRG(HWND hwnd)
|
||||
{
|
||||
int on = GetCheck(IDC_ENABLE);
|
||||
Enable(IDC_ALBUM, on);
|
||||
Enable(IDC_LIMITER, on);
|
||||
Enable(IDC_PREAMP, on);
|
||||
Enable(IDC_PA, on);
|
||||
}
|
||||
|
||||
static void UpdateDither(HWND hwnd)
|
||||
{
|
||||
int on = GetCheck(IDC_DITHERRG);
|
||||
Enable(IDC_SHAPE, on);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK OutputProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
@@ -71,12 +125,14 @@ static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
Check(IDC_LIMITER, flac_cfg.output.replaygain.hard_limit);
|
||||
Check(IDC_DITHER, flac_cfg.output.resolution.normal.dither_24_to_16);
|
||||
Check(IDC_DITHERRG, flac_cfg.output.resolution.replaygain.dither);
|
||||
// prepare preamp slider
|
||||
{
|
||||
HWND hamp = GetDlgItem(hwnd, IDC_PREAMP);
|
||||
SendMessage(hamp, TBM_SETRANGE, 1, MAKELONG(0, PREAMP_RANGE*2));
|
||||
SendMessage(hamp, TBM_SETPOS, 1, flac_cfg.output.replaygain.preamp+PREAMP_RANGE);
|
||||
UpdatePreamp(hwnd, hamp);
|
||||
}
|
||||
// fill comboboxes
|
||||
{
|
||||
HWND hlist = GetDlgItem(hwnd, IDC_TO);
|
||||
SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"16 bps");
|
||||
@@ -90,16 +146,15 @@ static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
SendMessage(hlist, CB_ADDSTRING, 0, (LPARAM)"High");
|
||||
SendMessage(hlist, CB_SETCURSEL, flac_cfg.output.resolution.replaygain.noise_shaping, 0);
|
||||
}
|
||||
UpdateRG(hwnd);
|
||||
UpdateDither(hwnd);
|
||||
return TRUE;
|
||||
// commands
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
// ok/cancel
|
||||
// ok
|
||||
case IDOK:
|
||||
if (thread_handle != INVALID_HANDLE_VALUE)
|
||||
SendMessage(mod_.hMainWindow, WM_COMMAND, WINAMP_BUTTON4, 0);
|
||||
|
||||
flac_cfg.output.replaygain.enable = GetCheck(IDC_ENABLE);
|
||||
flac_cfg.output.replaygain.album_mode = GetCheck(IDC_ALBUM);
|
||||
flac_cfg.output.replaygain.hard_limit = GetCheck(IDC_LIMITER);
|
||||
@@ -108,10 +163,8 @@ static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
flac_cfg.output.resolution.replaygain.dither = GetCheck(IDC_DITHERRG);
|
||||
flac_cfg.output.resolution.replaygain.noise_shaping = GetSel(IDC_SHAPE);
|
||||
flac_cfg.output.resolution.replaygain.bps_out = (GetSel(IDC_TO)+2)*8;
|
||||
/* fall through */
|
||||
case IDCANCEL:
|
||||
EndDialog(hwnd, LOWORD(wParam));
|
||||
return TRUE;
|
||||
break;
|
||||
// reset
|
||||
case IDC_RESET:
|
||||
Check(IDC_ENABLE, 1);
|
||||
Check(IDC_ALBUM, 0);
|
||||
@@ -120,10 +173,19 @@ static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
Check(IDC_DITHERRG, 0);
|
||||
|
||||
SendDlgItemMessage(hwnd, IDC_PREAMP, TBM_SETPOS, 1, PREAMP_RANGE);
|
||||
UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP));
|
||||
|
||||
SendDlgItemMessage(hwnd, IDC_TO, CB_SETCURSEL, 0, 0);
|
||||
SendDlgItemMessage(hwnd, IDC_SHAPE, CB_SETCURSEL, 1, 0);
|
||||
|
||||
UpdatePreamp(hwnd, GetDlgItem(hwnd, IDC_PREAMP));
|
||||
UpdateRG(hwnd);
|
||||
UpdateDither(hwnd);
|
||||
break;
|
||||
// active check-boxes
|
||||
case IDC_ENABLE:
|
||||
UpdateRG(hwnd);
|
||||
break;
|
||||
case IDC_DITHERRG:
|
||||
UpdateDither(hwnd);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -137,8 +199,144 @@ static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NUM_PAGES 2
|
||||
|
||||
int DoConfig(HWND parent)
|
||||
typedef struct
|
||||
{
|
||||
return DialogBox(mod_.hDllInstance, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc) == IDOK;
|
||||
HWND htab;
|
||||
HWND hdlg;
|
||||
RECT r;
|
||||
HWND all[NUM_PAGES];
|
||||
} LOCALDATA;
|
||||
|
||||
static void ScreenToClientRect(HWND hwnd, RECT *rect)
|
||||
{
|
||||
POINT pt = { rect->left, rect->top };
|
||||
ScreenToClient(hwnd, &pt);
|
||||
rect->left = pt.x;
|
||||
rect->top = pt.y;
|
||||
|
||||
pt.x = rect->right;
|
||||
pt.y = rect->bottom;
|
||||
ScreenToClient(hwnd, &pt);
|
||||
rect->right = pt.x;
|
||||
rect->bottom = pt.y;
|
||||
}
|
||||
|
||||
static void SendCommand(HWND hwnd, int command)
|
||||
{
|
||||
LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
|
||||
SendMessage(data->hdlg, WM_COMMAND, command, 0);
|
||||
}
|
||||
|
||||
static void BroadcastCommand(HWND hwnd, int command)
|
||||
{
|
||||
LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
|
||||
int i;
|
||||
|
||||
for (i=0; i<NUM_PAGES; i++)
|
||||
SendMessage(data->all[i], WM_COMMAND, command, 0);
|
||||
}
|
||||
|
||||
static void OnSelChange(HWND hwnd)
|
||||
{
|
||||
LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
|
||||
int index = TabCtrl_GetCurSel(data->htab);
|
||||
if (index < 0) return;
|
||||
// hide previous
|
||||
if (data->hdlg)
|
||||
ShowWindow(data->hdlg, SW_HIDE);
|
||||
// display
|
||||
data->hdlg = data->all[index];
|
||||
SetWindowPos(data->hdlg, HWND_TOP, data->r.left, data->r.top, data->r.right-data->r.left, data->r.bottom-data->r.top, SWP_SHOWWINDOW);
|
||||
}
|
||||
|
||||
static INT_PTR CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
static activePage = 0;
|
||||
|
||||
switch (msg)
|
||||
{
|
||||
// init
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));
|
||||
HINSTANCE inst = (HINSTANCE)lParam;
|
||||
TCITEM item;
|
||||
|
||||
// init
|
||||
SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);
|
||||
data->htab = GetDlgItem(hwnd, IDC_TABS);
|
||||
data->hdlg = NULL;
|
||||
// add pages
|
||||
item.mask = TCIF_TEXT;
|
||||
data->all[0] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_GENERAL), hwnd, GeneralProc);
|
||||
item.pszText = "General";
|
||||
TabCtrl_InsertItem(data->htab, 0, &item);
|
||||
|
||||
data->all[1] = CreateDialog(inst, MAKEINTRESOURCE(IDD_CONFIG_OUTPUT), hwnd, OutputProc);
|
||||
item.pszText = "Output";
|
||||
TabCtrl_InsertItem(data->htab, 1, &item);
|
||||
// get rect (after adding pages)
|
||||
GetWindowRect(data->htab, &data->r);
|
||||
ScreenToClientRect(hwnd, &data->r);
|
||||
TabCtrl_AdjustRect(data->htab, 0, &data->r);
|
||||
// simulate item change
|
||||
TabCtrl_SetCurSel(data->htab, activePage);
|
||||
OnSelChange(hwnd);
|
||||
}
|
||||
return TRUE;
|
||||
// destory
|
||||
case WM_DESTROY:
|
||||
{
|
||||
LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
|
||||
int i;
|
||||
|
||||
activePage = TabCtrl_GetCurSel(data->htab);
|
||||
|
||||
for (i=0; i<NUM_PAGES; i++)
|
||||
DestroyWindow(data->all[i]);
|
||||
|
||||
LocalFree(data);
|
||||
}
|
||||
break;
|
||||
// commands
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
// ok/cancel
|
||||
case IDOK:
|
||||
BroadcastCommand(hwnd, IDOK);
|
||||
/* fall through */
|
||||
case IDCANCEL:
|
||||
EndDialog(hwnd, LOWORD(wParam));
|
||||
return TRUE;
|
||||
case IDC_RESET:
|
||||
SendCommand(hwnd, IDC_RESET);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// notification
|
||||
case WM_NOTIFY:
|
||||
if (LOWORD(wParam) == IDC_TABS)
|
||||
{
|
||||
NMHDR *hdr = (NMHDR*)lParam;
|
||||
|
||||
switch (hdr->code)
|
||||
{
|
||||
case TCN_SELCHANGE:
|
||||
OnSelChange(hwnd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int DoConfig(HINSTANCE inst, HWND parent)
|
||||
{
|
||||
return DialogBoxParam(inst, MAKEINTRESOURCE(IDD_CONFIG), parent, DialogProc, (LONG)inst) == IDOK;
|
||||
}
|
||||
|
||||
@@ -4,41 +4,33 @@
|
||||
//
|
||||
|
||||
typedef struct {
|
||||
//!
|
||||
/*
|
||||
struct {
|
||||
gboolean tag_override;
|
||||
gchar *tag_format;
|
||||
gboolean convert_char_set;
|
||||
gchar *file_char_set;
|
||||
gchar *user_char_set;
|
||||
} title;
|
||||
*/
|
||||
|
||||
BOOL enable;
|
||||
BOOL album_mode;
|
||||
INT preamp;
|
||||
BOOL hard_limit;
|
||||
} replaygain;
|
||||
struct {
|
||||
struct {
|
||||
BOOL enable;
|
||||
BOOL album_mode;
|
||||
INT preamp;
|
||||
BOOL hard_limit;
|
||||
BOOL dither_24_to_16;
|
||||
} normal;
|
||||
struct {
|
||||
BOOL dither;
|
||||
INT noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */
|
||||
INT bps_out;
|
||||
} replaygain;
|
||||
struct {
|
||||
struct {
|
||||
BOOL dither_24_to_16;
|
||||
} normal;
|
||||
struct {
|
||||
BOOL dither;
|
||||
INT noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */
|
||||
INT bps_out;
|
||||
} replaygain;
|
||||
} resolution;
|
||||
} output;
|
||||
} resolution;
|
||||
} output_config_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
char tag_format[256];
|
||||
} title;
|
||||
output_config_t output;
|
||||
} flac_config_t;
|
||||
|
||||
extern flac_config_t flac_cfg;
|
||||
extern char ini_name[MAX_PATH];
|
||||
extern HANDLE thread_handle;
|
||||
extern In_Module mod_;
|
||||
|
||||
//
|
||||
// prototypes
|
||||
@@ -46,4 +38,4 @@ extern In_Module mod_;
|
||||
|
||||
void ReadConfig();
|
||||
void WriteConfig();
|
||||
int DoConfig(HWND parent);
|
||||
int DoConfig(HINSTANCE inst, HWND parent);
|
||||
|
||||
@@ -27,27 +27,23 @@
|
||||
#include "plugin_common/all.h"
|
||||
#include "share/grabbag.h"
|
||||
#include "config.h"
|
||||
#include "infobox.h"
|
||||
#include "tagz.h"
|
||||
|
||||
|
||||
BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* post this to the main window at end of file (after playback as stopped) */
|
||||
#define WM_WA_MPEG_EOF WM_USER+2
|
||||
|
||||
typedef struct {
|
||||
FLAC__bool abort_flag;
|
||||
int seek_to;
|
||||
int paused;
|
||||
unsigned total_samples;
|
||||
unsigned bits_per_sample;
|
||||
unsigned output_bits_per_sample;
|
||||
unsigned channels;
|
||||
unsigned sample_rate;
|
||||
unsigned length_in_msec;
|
||||
DitherContext dither_context;
|
||||
FLAC__bool has_replaygain;
|
||||
double replay_scale;
|
||||
DitherContext dither_context;
|
||||
} file_info_struct;
|
||||
|
||||
|
||||
@@ -59,14 +55,13 @@ static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__Str
|
||||
static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
|
||||
static void get_description_(const char *filename, char *description, unsigned max_size);
|
||||
|
||||
In_Module mod_; /* the input module (declared near the bottom of this file) */
|
||||
char ini_name[MAX_PATH];
|
||||
flac_config_t flac_cfg;
|
||||
static output_config_t cfg; /* local copy */
|
||||
|
||||
static In_Module mod_; /* the input module (declared near the bottom of this file) */
|
||||
static char lastfn_[MAX_PATH]; /* currently playing file (used for getting info on the current file) */
|
||||
static int decode_pos_ms_; /* current decoding position, in milliseconds */
|
||||
static int paused_; /* are we paused? */
|
||||
static int seek_needed_; /* if != -1, it is the point that the decode thread should seek to, in ms. */
|
||||
|
||||
#define SAMPLES_PER_WRITE 576
|
||||
static FLAC__int32 reservoir_[FLAC__MAX_BLOCK_SIZE * 2/*for overflow*/ * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS];
|
||||
@@ -76,14 +71,24 @@ static file_info_struct file_info_;
|
||||
static FLAC__FileDecoder *decoder_;
|
||||
|
||||
static volatile int killDecodeThread = 0; /* the kill switch for the decode thread */
|
||||
HANDLE thread_handle = INVALID_HANDLE_VALUE; /* the handle to the decode thread */
|
||||
static HANDLE thread_handle = NULL; /* the handle to the decode thread */
|
||||
|
||||
static DWORD WINAPI DecodeThread(void *b); /* the decode thread procedure */
|
||||
|
||||
|
||||
static void show_error(const char *message,...)
|
||||
{
|
||||
char foo[512];
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
vsprintf(foo, message, args);
|
||||
va_end(args);
|
||||
MessageBox(mod_.hMainWindow, foo, "FLAC Plug-in Error", MB_ICONSTOP);
|
||||
}
|
||||
|
||||
void config(HWND hwndParent)
|
||||
{
|
||||
if (DoConfig(hwndParent))
|
||||
if (DoConfig(mod_.hDllInstance, hwndParent))
|
||||
WriteConfig();
|
||||
}
|
||||
|
||||
@@ -98,7 +103,7 @@ void init()
|
||||
|
||||
decoder_ = FLAC__file_decoder_new();
|
||||
strcpy(lastfn_, "");
|
||||
// read config
|
||||
/* read config */
|
||||
GetModuleFileName(NULL, ini_name, sizeof(ini_name));
|
||||
p = strrchr(ini_name, '.');
|
||||
if (!p) p = ini_name + strlen(ini_name);
|
||||
@@ -115,85 +120,91 @@ void quit()
|
||||
}
|
||||
|
||||
int isourfile(char *fn) { return 0; }
|
||||
/* used for detecting URL streams.. unused here. strncmp(fn, "http://", 7) to detect HTTP streams, etc */
|
||||
|
||||
|
||||
int play(char *fn)
|
||||
{
|
||||
int maxlatency;
|
||||
int thread_id;
|
||||
HANDLE input_file;
|
||||
DWORD file_size; /*@@@ fixme 64-bit */
|
||||
|
||||
if(0 == decoder_)
|
||||
if (decoder_ == 0)
|
||||
return 1;
|
||||
|
||||
input_file = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if(input_file == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
file_size = GetFileSize(input_file, NULL);
|
||||
CloseHandle(input_file);
|
||||
|
||||
file_info_.abort_flag = false;
|
||||
file_info_.has_replaygain = false;
|
||||
if(!safe_decoder_init_(fn, decoder_))
|
||||
if (!safe_decoder_init_(fn, decoder_))
|
||||
return 1;
|
||||
|
||||
cfg = flac_cfg.output;
|
||||
strcpy(lastfn_, fn);
|
||||
wide_samples_in_reservoir_ = 0;
|
||||
file_info_.output_bits_per_sample = file_info_.has_replaygain && flac_cfg.output.replaygain.enable ?
|
||||
flac_cfg.output.resolution.replaygain.dither ? flac_cfg.output.resolution.replaygain.bps_out : file_info_.bits_per_sample :
|
||||
flac_cfg.output.resolution.normal.dither_24_to_16 ? min(file_info_.bits_per_sample, 16) : file_info_.bits_per_sample;
|
||||
file_info_.output_bits_per_sample = file_info_.has_replaygain && cfg.replaygain.enable ?
|
||||
cfg.resolution.replaygain.bps_out :
|
||||
cfg.resolution.normal.dither_24_to_16 ? min(file_info_.bits_per_sample, 16) : file_info_.bits_per_sample;
|
||||
|
||||
if (file_info_.has_replaygain && flac_cfg.output.replaygain.enable && flac_cfg.output.resolution.replaygain.dither)
|
||||
FLAC__plugin_common__init_dither_context(&file_info_.dither_context, file_info_.bits_per_sample, flac_cfg.output.resolution.replaygain.noise_shaping);
|
||||
if (file_info_.has_replaygain && cfg.replaygain.enable && cfg.resolution.replaygain.dither)
|
||||
FLAC__plugin_common__init_dither_context(&file_info_.dither_context, file_info_.bits_per_sample, cfg.resolution.replaygain.noise_shaping);
|
||||
|
||||
maxlatency = mod_.outMod->Open(file_info_.sample_rate, file_info_.channels, file_info_.output_bits_per_sample, -1, -1);
|
||||
if(maxlatency < 0) /* error opening device */
|
||||
if (maxlatency < 0) /* error opening device */
|
||||
return 1;
|
||||
|
||||
/* dividing by 1000 for the first parameter of setinfo makes it */
|
||||
/* display 'H'... for hundred.. i.e. 14H Kbps. */
|
||||
mod_.SetInfo((file_info_.sample_rate*file_info_.bits_per_sample*file_info_.channels)/1000, file_info_.sample_rate/1000, file_info_.channels, 1);
|
||||
mod_.SetInfo((int)(file_size/(125.*file_info_.total_samples/file_info_.sample_rate)), file_info_.sample_rate/1000, file_info_.channels, 1);
|
||||
|
||||
/* initialize vis stuff */
|
||||
mod_.SAVSAInit(maxlatency, file_info_.sample_rate);
|
||||
mod_.VSASetInfo(file_info_.sample_rate, file_info_.channels);
|
||||
/* set the output plug-ins default volume */
|
||||
mod_.outMod->SetVolume(-666);
|
||||
|
||||
paused_ = 0;
|
||||
file_info_.paused = 0;
|
||||
file_info_.seek_to = -1;
|
||||
decode_pos_ms_ = 0;
|
||||
seek_needed_ = -1;
|
||||
killDecodeThread = 0;
|
||||
thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) DecodeThread, NULL, 0, &thread_id);
|
||||
thread_handle = CreateThread(NULL, 0, DecodeThread, NULL, 0, &thread_id);
|
||||
if (!thread_handle)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pause()
|
||||
{
|
||||
paused_ = 1;
|
||||
file_info_.paused = 1;
|
||||
mod_.outMod->Pause(1);
|
||||
}
|
||||
|
||||
void unpause()
|
||||
{
|
||||
paused_ = 0;
|
||||
file_info_.paused = 0;
|
||||
mod_.outMod->Pause(0);
|
||||
}
|
||||
|
||||
int ispaused()
|
||||
{
|
||||
return paused_;
|
||||
return file_info_.paused;
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
if(thread_handle != INVALID_HANDLE_VALUE) {
|
||||
if (thread_handle) {
|
||||
killDecodeThread = 1;
|
||||
if(WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT) {
|
||||
MessageBox(mod_.hMainWindow, "error asking thread to die!\n", "error killing decode thread", 0);
|
||||
show_error("Error while stopping decoding thread.");
|
||||
TerminateThread(thread_handle, 0);
|
||||
}
|
||||
CloseHandle(thread_handle);
|
||||
thread_handle = INVALID_HANDLE_VALUE;
|
||||
thread_handle = NULL;
|
||||
}
|
||||
safe_decoder_finish_(decoder_);
|
||||
|
||||
@@ -214,7 +225,7 @@ int getoutputtime()
|
||||
|
||||
void setoutputtime(int time_in_ms)
|
||||
{
|
||||
seek_needed_ = time_in_ms;
|
||||
file_info_.seek_to = time_in_ms;
|
||||
}
|
||||
|
||||
void setvolume(int volume) { mod_.outMod->SetVolume(volume); }
|
||||
@@ -222,15 +233,7 @@ void setpan(int pan) { mod_.outMod->SetPan(pan); }
|
||||
|
||||
int infoDlg(char *fn, HWND hwnd)
|
||||
{
|
||||
/* @@@TODO: implement info dialog. */
|
||||
if (!stricmp(fn, lastfn_)) {
|
||||
char buffer[512];
|
||||
sprintf(buffer, "%s\nLength: %d:%02d, ReplayGain: %spresent\n%dHz, %d channel(s), %dbps (%dbps on output)",
|
||||
lastfn_, file_info_.length_in_msec/60000, (file_info_.length_in_msec/1000)%60, file_info_.has_replaygain ? "" : "not ",
|
||||
file_info_.sample_rate, file_info_.channels, file_info_.bits_per_sample, file_info_.output_bits_per_sample);
|
||||
MessageBox(hwnd, buffer, "FLAC Info", 0);
|
||||
}
|
||||
|
||||
DoInfoBox(mod_.hDllInstance, hwnd, fn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -240,27 +243,24 @@ void getfileinfo(char *filename, char *title, int *length_in_msec)
|
||||
|
||||
if (!filename || !*filename) {
|
||||
filename = lastfn_;
|
||||
if(length_in_msec) {
|
||||
if (length_in_msec) {
|
||||
*length_in_msec = getlength();
|
||||
length_in_msec = 0; /* force skip in following code */
|
||||
}
|
||||
}
|
||||
|
||||
if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
|
||||
MessageBox(mod_.hMainWindow, filename, "ERROR: invalid/missing FLAC metadata", 0);
|
||||
if(title) {
|
||||
static const char *errtitle = "Invalid FLAC File: ";
|
||||
sprintf(title, "%s\"%s\"", errtitle, filename);
|
||||
}
|
||||
if(length_in_msec)
|
||||
if (!FLAC__metadata_get_streaminfo(filename, &streaminfo)) {
|
||||
if (title)
|
||||
sprintf(title, "Invalid FLAC: %s", filename);
|
||||
if (length_in_msec)
|
||||
*length_in_msec = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if(title) {
|
||||
get_description_(filename, title, MAX_PATH);
|
||||
}
|
||||
if(length_in_msec)
|
||||
if (title)
|
||||
get_description_(filename, title, 400);
|
||||
|
||||
if (length_in_msec)
|
||||
*length_in_msec = (int)(streaminfo.data.stream_info.total_samples * 10 / (streaminfo.data.stream_info.sample_rate / 100));
|
||||
}
|
||||
|
||||
@@ -314,12 +314,12 @@ static DWORD WINAPI DecodeThread(void *unused)
|
||||
const unsigned target_bps = file_info_.output_bits_per_sample;
|
||||
const unsigned sample_rate = file_info_.sample_rate;
|
||||
|
||||
if(seek_needed_ != -1) {
|
||||
const double distance = (double)seek_needed_ / (double)getlength();
|
||||
if(file_info_.seek_to != -1) {
|
||||
const double distance = (double)file_info_.seek_to / (double)getlength();
|
||||
const unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples);
|
||||
if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) {
|
||||
decode_pos_ms_ = (int)(distance * (double)getlength());
|
||||
seek_needed_ = -1;
|
||||
file_info_.seek_to = -1;
|
||||
done = 0;
|
||||
mod_.outMod->Flush(decode_pos_ms_);
|
||||
}
|
||||
@@ -338,7 +338,7 @@ static DWORD WINAPI DecodeThread(void *unused)
|
||||
break;
|
||||
}
|
||||
else if(!FLAC__file_decoder_process_single(decoder_)) {
|
||||
MessageBox(mod_.hMainWindow, FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder_)], "READ ERROR processing frame", 0);
|
||||
show_error("Error while processing frame (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder_)]);
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
@@ -353,7 +353,7 @@ static DWORD WINAPI DecodeThread(void *unused)
|
||||
int bytes;
|
||||
unsigned i;
|
||||
|
||||
if(flac_cfg.output.replaygain.enable && file_info_.has_replaygain) {
|
||||
if(cfg.replaygain.enable && file_info_.has_replaygain) {
|
||||
bytes = (int)FLAC__plugin_common__apply_gain(
|
||||
sample_buffer_,
|
||||
reservoir_,
|
||||
@@ -362,9 +362,9 @@ static DWORD WINAPI DecodeThread(void *unused)
|
||||
bits_per_sample,
|
||||
target_bps,
|
||||
(float)file_info_.replay_scale,
|
||||
flac_cfg.output.replaygain.hard_limit,
|
||||
flac_cfg.output.resolution.replaygain.dither,
|
||||
(NoiseShaping)flac_cfg.output.resolution.replaygain.noise_shaping,
|
||||
cfg.replaygain.hard_limit,
|
||||
cfg.resolution.replaygain.dither,
|
||||
(NoiseShaping)cfg.resolution.replaygain.noise_shaping,
|
||||
&file_info_.dither_context
|
||||
);
|
||||
}
|
||||
@@ -400,7 +400,7 @@ static DWORD WINAPI DecodeThread(void *unused)
|
||||
In_Module mod_ =
|
||||
{
|
||||
IN_VER,
|
||||
"Reference FLAC Player v" VERSION,
|
||||
"FLAC Reference Player v" VERSION,
|
||||
0, /* hMainWindow */
|
||||
0, /* hDllInstance */
|
||||
"FLAC\0FLAC Audio File (*.FLAC)\0"
|
||||
@@ -440,21 +440,22 @@ In_Module mod_ =
|
||||
|
||||
};
|
||||
|
||||
__declspec( dllexport ) In_Module * winampGetInModule2()
|
||||
__declspec(dllexport) In_Module *winampGetInModule2()
|
||||
{
|
||||
return &mod_;
|
||||
}
|
||||
|
||||
BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* local routines
|
||||
**********************************************************************/
|
||||
FLAC__bool safe_decoder_init_(const char *filename, FLAC__FileDecoder *decoder)
|
||||
{
|
||||
if(decoder == 0) {
|
||||
MessageBox(mod_.hMainWindow, "Decoder instance is NULL", "ERROR initializing decoder", 0);
|
||||
return false;
|
||||
}
|
||||
FLAC__ASSERT(0 != decoder);
|
||||
|
||||
safe_decoder_finish_(decoder);
|
||||
|
||||
@@ -467,18 +468,19 @@ FLAC__bool safe_decoder_init_(const char *filename, FLAC__FileDecoder *decoder)
|
||||
FLAC__file_decoder_set_write_callback(decoder, write_callback_);
|
||||
FLAC__file_decoder_set_error_callback(decoder, error_callback_);
|
||||
FLAC__file_decoder_set_client_data(decoder, &file_info_);
|
||||
|
||||
if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) {
|
||||
MessageBox(mod_.hMainWindow, FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)], "ERROR initializing decoder", 0);
|
||||
show_error("Error while initializing decoder (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!FLAC__file_decoder_process_until_end_of_metadata(decoder)) {
|
||||
MessageBox(mod_.hMainWindow, FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)], "ERROR processing metadata", 0);
|
||||
show_error("Error while processing metadata (%s).", FLAC__FileDecoderStateString[FLAC__file_decoder_get_state(decoder)]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(file_info_.abort_flag)
|
||||
return false; /* metadata callback already popped up the error dialog */
|
||||
if (file_info_.abort_flag)
|
||||
return false; /* metadata callback already popped up the error dialog */
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -524,13 +526,13 @@ void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMeta
|
||||
|
||||
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
|
||||
FLAC__ASSERT(metadata->data.stream_info.total_samples < 0x100000000); /* this plugin can only handle < 4 gigasamples */
|
||||
file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff);
|
||||
file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xfffffffful);
|
||||
file_info->bits_per_sample = metadata->data.stream_info.bits_per_sample;
|
||||
file_info->channels = metadata->data.stream_info.channels;
|
||||
file_info->sample_rate = metadata->data.stream_info.sample_rate;
|
||||
|
||||
if(file_info->bits_per_sample != 8 && file_info->bits_per_sample != 16 && file_info->bits_per_sample != 24) {
|
||||
MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16/24-bit samples\n", "ERROR: plugin can only handle 8/16/24-bit samples", 0);
|
||||
if(file_info->bits_per_sample!=8 && file_info->bits_per_sample!=16 && file_info->bits_per_sample!=24) {
|
||||
show_error("This plugin can only handle 8/16/24-bit samples.");
|
||||
file_info->abort_flag = true;
|
||||
return;
|
||||
}
|
||||
@@ -538,71 +540,101 @@ void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMeta
|
||||
}
|
||||
else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
|
||||
double gain, peak;
|
||||
if(grabbag__replaygain_load_from_vorbiscomment(metadata, flac_cfg.output.replaygain.album_mode, &gain, &peak)) {
|
||||
if(grabbag__replaygain_load_from_vorbiscomment(metadata, cfg.replaygain.album_mode, &gain, &peak)) {
|
||||
file_info_.has_replaygain = true;
|
||||
file_info_.replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)flac_cfg.output.replaygain.preamp, /*prevent_clipping=*/!flac_cfg.output.replaygain.hard_limit);
|
||||
file_info_.replay_scale = grabbag__replaygain_compute_scale_factor(peak, gain, (double)cfg.replaygain.preamp, !cfg.replaygain.hard_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||
{
|
||||
file_info_struct *file_info = (file_info_struct *)client_data;
|
||||
file_info_struct *file_info = (file_info_struct*)client_data;
|
||||
(void)decoder;
|
||||
if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
|
||||
file_info->abort_flag = true;
|
||||
}
|
||||
|
||||
static FLAC__bool local__is_blank(const char *s)
|
||||
/*
|
||||
* title formatting
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
return 0 == s || *s == '\0';
|
||||
FLAC_Plugin__CanonicalTag t;
|
||||
const char *filename;
|
||||
} tag_param_t;
|
||||
|
||||
|
||||
static __inline char *GetFileName(const char *fullname)
|
||||
{
|
||||
const char *c = fullname + strlen(fullname) - 1;
|
||||
|
||||
while (c > fullname)
|
||||
{
|
||||
if (*c=='\\' || *c=='/')
|
||||
{
|
||||
c++;
|
||||
break;
|
||||
}
|
||||
c--;
|
||||
}
|
||||
|
||||
return (char*)c;
|
||||
}
|
||||
|
||||
static const T_CHAR *get_tag(const T_CHAR *tag, void *param)
|
||||
{
|
||||
tag_param_t *p = (tag_param_t*)param;
|
||||
|
||||
if (!stricmp(tag, "path") || !stricmp(tag, "filepath") || !stricmp(tag, "url"))
|
||||
return p->filename;
|
||||
else if (!stricmp(tag, "filename"))
|
||||
{
|
||||
static char foo[MAX_PATH];
|
||||
char *c;
|
||||
|
||||
strcpy(foo, GetFileName(p->filename));
|
||||
if (c = strrchr(foo, '.')) *c = 0;
|
||||
|
||||
return foo;
|
||||
}
|
||||
else if (!stricmp(tag, "title"))
|
||||
return p->t.title;
|
||||
else if (!stricmp(tag, "artist"))
|
||||
return p->t.performer ? p->t.performer : p->t.composer;
|
||||
else if (!stricmp(tag, "composer"))
|
||||
return p->t.composer;
|
||||
else if (!stricmp(tag, "performer"))
|
||||
return p->t.performer;
|
||||
else if (!stricmp(tag, "album"))
|
||||
return p->t.album;
|
||||
else if (!stricmp(tag, "year") || !stricmp(tag, "date"))
|
||||
return p->t.year_recorded ? p->t.year_recorded : p->t.year_performed;
|
||||
else if (!stricmp(tag, "year_recorded"))
|
||||
return p->t.year_recorded;
|
||||
else if (!stricmp(tag, "year_performed"))
|
||||
return p->t.year_performed;
|
||||
else if (!stricmp(tag, "track_number"))
|
||||
return p->t.track_number;
|
||||
else if (!stricmp(tag, "tracks_in_album"))
|
||||
return p->t.tracks_in_album;
|
||||
else if (!stricmp(tag, "genre"))
|
||||
return p->t.genre;
|
||||
else if (!stricmp(tag, "comment") || !stricmp(tag, "description"))
|
||||
return p->t.comment;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
void get_description_(const char *filename, char *description, unsigned max_size)
|
||||
{
|
||||
FLAC_Plugin__CanonicalTag tag;
|
||||
tag_param_t param;
|
||||
|
||||
FLAC_plugin__canonical_tag_init(&tag);
|
||||
FLAC_plugin__canonical_tag_get_combined(filename, &tag);
|
||||
FLAC_plugin__canonical_tag_init(¶m.t);
|
||||
FLAC_plugin__canonical_tag_get_combined(filename, ¶m.t);
|
||||
param.filename = filename;
|
||||
|
||||
/* @@@ when config window is done, add code here for converting to user charset ala plugin_xmms */
|
||||
tagz_format(flac_cfg.title.tag_format, get_tag, NULL, ¶m, description, max_size);
|
||||
|
||||
if(local__is_blank(tag.performer) && local__is_blank(tag.composer) && local__is_blank(tag.title)) {
|
||||
/* set the description to the filename */
|
||||
char *p;
|
||||
const char *temp = strrchr(filename, '\\');
|
||||
if(0 == temp)
|
||||
temp = strrchr(filename, '/');
|
||||
if(0 == temp)
|
||||
temp = filename;
|
||||
else
|
||||
temp++;
|
||||
strncpy(description, temp, max_size);
|
||||
description[max_size-1] = '\0';
|
||||
if(0 != (p = strrchr(description, '.')))
|
||||
*p = '\0';
|
||||
}
|
||||
else {
|
||||
char *artist = !local__is_blank(tag.performer)? tag.performer : !local__is_blank(tag.composer)? tag.composer : "Unknown Artist";
|
||||
char *title = !local__is_blank(tag.title)? tag.title : "Untitled";
|
||||
|
||||
/* there's no snprintf in VC6 so we get sloppy */
|
||||
#if 0
|
||||
snprintf(description, max_size, "%s - %s", artist, title);
|
||||
#else
|
||||
const unsigned needed = strlen(artist) + strlen(title) + 3 + 1;
|
||||
if(needed <= max_size)
|
||||
sprintf(description, "%s - %s", artist, title);
|
||||
else {
|
||||
char *p = malloc(needed);
|
||||
if(0 != p) {
|
||||
sprintf(p, "%s - %s", artist, title);
|
||||
p[max_size-1] = '\0';
|
||||
strcpy(description, p);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FLAC_plugin__canonical_tag_clear(&tag);
|
||||
FLAC_plugin__canonical_tag_clear(¶m.t);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ RSC=rc.exe
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "in_flac_EXPORTS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /Ox /Og /Oi /Os /Op /Ob1 /I "include" /I ".." /I "..\..\include" /D "NDEBUG" /D VERSION=\"1.0.5_beta2\" /D "in_flac_EXPORTS" /D "FLAC__NO_DLL" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /Ox /Og /Oi /Os /Op /Gf /Gy /I "include" /I ".." /I "..\..\include" /D "NDEBUG" /D VERSION=\"1.0.5_beta2\" /D "in_flac_EXPORTS" /D "FLAC__NO_DLL" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
|
||||
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
@@ -53,7 +53,7 @@ BSC32=bscmake.exe
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
|
||||
# ADD LINK32 plugin_common_static.lib grabbag_static.lib libFLAC_static.lib gain_analysis_static.lib kernel32.lib user32.lib /nologo /dll /machine:I386 /nodefaultlib:"libc.lib" /out:"../../obj/release/bin/in_flac.dll" /libpath:"../../obj/release/lib" /opt:nowin98
|
||||
# ADD LINK32 plugin_common_static.lib grabbag_static.lib libFLAC_static.lib gain_analysis_static.lib kernel32.lib user32.lib /nologo /dll /machine:I386 /out:"../../obj/release/bin/in_flac.dll" /libpath:"../../obj/release/lib" /opt:nowin98
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ELSEIF "$(CFG)" == "in_flac - Win32 Debug"
|
||||
@@ -80,7 +80,7 @@ BSC32=bscmake.exe
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 ..\..\obj\debug\lib\plugin_common_static.lib ..\..\obj\debug\lib\grabbag_static.lib ..\..\obj\debug\lib\libFLAC_static.lib kernel32.lib user32.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"libcd.lib" /out:"../../obj/debug/bin/in_flac.dll" /pdbtype:sept /libpath:"../../obj/debug/lib"
|
||||
# ADD LINK32 plugin_common_static.lib grabbag_static.lib libFLAC_static.lib gain_analysis_static.lib kernel32.lib user32.lib /nologo /dll /debug /machine:I386 /out:"../../obj/debug/bin/in_flac.dll" /pdbtype:sept /libpath:"../../obj/debug/lib"
|
||||
# SUBTRACT LINK32 /pdb:none
|
||||
|
||||
!ENDIF
|
||||
@@ -97,11 +97,11 @@ LINK32=link.exe
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE="in2.h"
|
||||
SOURCE=.\include\winamp2\in2.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE="out.h"
|
||||
SOURCE=.\include\winamp2\out.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Source File
|
||||
@@ -118,8 +118,28 @@ SOURCE=.\in_flac.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\infobox.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\infobox.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\resource.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\resource.rc
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tagz.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\tagz.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Target
|
||||
# End Project
|
||||
|
||||
@@ -16,6 +16,5 @@
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
EXTRA_DIST = \
|
||||
frontend.h \
|
||||
in2.h \
|
||||
out.h
|
||||
|
||||
@@ -1,441 +0,0 @@
|
||||
#ifndef _WAFE_H_
|
||||
#define _WAFE_H_
|
||||
/*
|
||||
** Winamp frontend/plug-in control API documentation v1.1.
|
||||
** By Justin Frankel. Updates by Christophe Thibault.
|
||||
** Copyright (C) 1997-2000, Nullsoft Inc.
|
||||
** Last updated: JUL.12.2000.
|
||||
**
|
||||
** Introduction
|
||||
** -----------------------
|
||||
** This file describes a means to easily communicate to Winamp
|
||||
** via the classic Win32 Message API.
|
||||
**
|
||||
** These definitions/code assume C/C++. Porting to VB/Delphi shouldn't
|
||||
** be too hard.
|
||||
**
|
||||
** First, you find the HWND of the Winamp main window. From a plug-in
|
||||
** you can easily extract this from the plug-in structure (hMainWindow,
|
||||
** hwndParent, whatever). For external apps, use:
|
||||
**
|
||||
** HWND hwnd_winamp = FindWindow("Winamp v1.x",NULL);
|
||||
**
|
||||
** (note: I know, we're in Winamp 2.x, but it's 1.x for compatibility)
|
||||
**
|
||||
** Once you have the hwnd_winamp, it's a good idea to check the version
|
||||
** number. To do this, you send a WM_WA_IPC message to hwnd_winamp.
|
||||
** Note that WM_WA_IPC is defined as Win32's WM_USER.
|
||||
**
|
||||
** Note that sometimes you might want to use PostMessage instead of
|
||||
** SendMessage.
|
||||
*/
|
||||
|
||||
#define WM_WA_IPC WM_USER
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
#define IPC_GETVERSION 0
|
||||
|
||||
/*
|
||||
** int version = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION);
|
||||
**
|
||||
** Version will be 0x20yx for winamp 2.yx. versions previous to Winamp 2.0
|
||||
** typically (but not always) use 0x1zyx for 1.zx versions. Weird, I know.
|
||||
**
|
||||
** The basic format for sending messages to Winamp is:
|
||||
** int result=SendMessage(hwnd_winamp,WM_WA_IPC,command_data,command);
|
||||
** (for the version check, command_data is 0).
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_DELETE 101
|
||||
|
||||
/*
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_DELETE);
|
||||
**
|
||||
** You can use IPC_DELETE to clear Winamp's internal playlist.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_STARTPLAY 102
|
||||
|
||||
/*
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_STARTPLAY);
|
||||
**
|
||||
** Using IPC_STARTPLAY is like hitting 'Play' in Winamp, mostly.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_ISPLAYING 104
|
||||
|
||||
/*
|
||||
** int res = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING);
|
||||
**
|
||||
** IPC_ISPLAYING returns the status of playback.
|
||||
** If it returns 1, it is playing. if it returns 3, it is paused,
|
||||
** if it returns 0, it is not playing.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETOUTPUTTIME 105
|
||||
|
||||
/*
|
||||
** int res = SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETOUTPUTTIME);
|
||||
**
|
||||
** IPC_GETOUTPUTTIME returns the position in milliseconds of the
|
||||
** current song (mode = 0), or the song length, in seconds (mode = 1).
|
||||
** Returns -1 if not playing or error.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_JUMPTOTIME 106
|
||||
|
||||
/* (requires Winamp 1.60+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,ms,IPC_JUMPTOTIME);
|
||||
** IPC_JUMPTOTIME sets the position in milliseconds of the
|
||||
** current song (approximately).
|
||||
** Returns -1 if not playing, 1 on eof, or 0 if successful
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_WRITEPLAYLIST 120
|
||||
|
||||
/* (requires Winamp 1.666+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_WRITEPLAYLIST);
|
||||
**
|
||||
** IPC_WRITEPLAYLIST writes the current playlist to <winampdir>\\Winamp.m3u,
|
||||
** and returns the current playlist position.
|
||||
** Kinda obsoleted by some of the 2.x new stuff, but still good for when
|
||||
** using a front-end (instead of a plug-in)
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_SETPLAYLISTPOS 121
|
||||
|
||||
/* (requires Winamp 2.0+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,position,IPC_SETPLAYLISTPOS)
|
||||
**
|
||||
** IPC_SETPLAYLISTPOS sets the playlsit position to 'position'.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_SETVOLUME 122
|
||||
|
||||
/* (requires Winamp 2.0+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,volume,IPC_SETVOLUME);
|
||||
**
|
||||
** IPC_SETVOLUME sets the volume of Winamp (from 0-255).
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_SETPANNING 123
|
||||
|
||||
/* (requires Winamp 2.0+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,panning,IPC_SETPANNING);
|
||||
**
|
||||
** IPC_SETPANNING sets the panning of Winamp (from 0 (left) to 255 (right)).
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETLISTLENGTH 124
|
||||
|
||||
/* (requires Winamp 2.0+)
|
||||
** int length = SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTLENGTH);
|
||||
**
|
||||
** IPC_GETLISTLENGTH returns the length of the current playlist, in
|
||||
** tracks.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_SETSKIN 200
|
||||
|
||||
/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)"skinname",IPC_SETSKIN);
|
||||
**
|
||||
** IPC_SETSKIN sets the current skin to "skinname". Note that skinname
|
||||
** can be the name of a skin, a skin .zip file, with or without path.
|
||||
** If path isn't specified, the default search path is the winamp skins
|
||||
** directory.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETSKIN 201
|
||||
|
||||
/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)skinname_buffer,IPC_GETSKIN);
|
||||
**
|
||||
** IPC_GETSKIN puts the directory where skin bitmaps can be found
|
||||
** into skinname_buffer.
|
||||
** skinname_buffer must be MAX_PATH characters in length.
|
||||
** When using a .zip'd skin file, it'll return a temporary directory
|
||||
** where the ZIP was decompressed.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_EXECPLUG 202
|
||||
|
||||
/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)"vis_file.dll",IPC_EXECPLUG);
|
||||
**
|
||||
** IPC_EXECPLUG executes a visualization plug-in pointed to by WPARAM.
|
||||
** the format of this string can be:
|
||||
** "vis_whatever.dll"
|
||||
** "vis_whatever.dll,0" // (first mod, file in winamp plug-in dir)
|
||||
** "C:\\dir\\vis_whatever.dll,1"
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETPLAYLISTFILE 211
|
||||
|
||||
/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))
|
||||
** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTFILE);
|
||||
**
|
||||
** IPC_GETPLAYLISTFILE gets the filename of the playlist entry [index].
|
||||
** returns a pointer to it. returns NULL on error.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETPLAYLISTTITLE 212
|
||||
|
||||
/* (requires Winamp 2.04+, only usable from plug-ins (not external apps))
|
||||
** char *name=SendMessage(hwnd_winamp,WM_WA_IPC,index,IPC_GETPLAYLISTTITLE);
|
||||
**
|
||||
** IPC_GETPLAYLISTTITLE gets the title of the playlist entry [index].
|
||||
** returns a pointer to it. returns NULL on error.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETLISTPOS 125
|
||||
|
||||
/* (requires Winamp 2.05+)
|
||||
** int pos=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GETLISTPOS);
|
||||
**
|
||||
** IPC_GETLISTPOS returns the playlist position. A lot like IPC_WRITEPLAYLIST
|
||||
** only faster since it doesn't have to write out the list. Heh, silly me.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETINFO 126
|
||||
|
||||
/* (requires Winamp 2.05+)
|
||||
** int inf=SendMessage(hwnd_winamp,WM_WA_IPC,mode,IPC_GETINFO);
|
||||
**
|
||||
** IPC_GETINFO returns info about the current playing song. The value
|
||||
** it returns depends on the value of 'mode'.
|
||||
** Mode Meaning
|
||||
** ------------------
|
||||
** 0 Samplerate (i.e. 44100)
|
||||
** 1 Bitrate (i.e. 128)
|
||||
** 2 Channels (i.e. 2)
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_GETEQDATA 127
|
||||
|
||||
/* (requires Winamp 2.05+)
|
||||
** int data=SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA);
|
||||
**
|
||||
** IPC_GETEQDATA queries the status of the EQ.
|
||||
** The value returned depends on what 'pos' is set to:
|
||||
** Value Meaning
|
||||
** ------------------
|
||||
** 0-9 The 10 bands of EQ data. 0-63 (+20db - -20db)
|
||||
** 10 The preamp value. 0-63 (+20db - -20db)
|
||||
** 11 Enabled. zero if disabled, nonzero if enabled.
|
||||
** 12 Autoload. zero if disabled, nonzero if enabled.
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_SETEQDATA 128
|
||||
/* (requires Winamp 2.05+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,pos,IPC_GETEQDATA);
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SETEQDATA);
|
||||
**
|
||||
** IPC_SETEQDATA sets the value of the last position retrieved
|
||||
** by IPC_GETEQDATA.
|
||||
*/
|
||||
|
||||
#define IPC_ADDBOOKMARK 129
|
||||
/* (requires Winamp 2.4+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_ADDBOOKMARK);
|
||||
**
|
||||
** IPC_ADDBOOKMARK will add the specified file to the Winamp bookmark list.
|
||||
*/
|
||||
|
||||
#define IPC_RESTARTWINAMP 135
|
||||
/* (requires Winamp 2.2+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_RESTARTWINAMP);
|
||||
**
|
||||
** IPC_RESTARTWINAMP will restart Winamp (isn't that obvious ? :)
|
||||
*/
|
||||
|
||||
#define IPC_MBOPEN 241
|
||||
/* (requires Winamp 2.05+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_MBOPEN);
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPEN);
|
||||
**
|
||||
** IPC_MBOPEN will open a new URL in the minibrowser. if url is NULL, it will open the Minibrowser window.
|
||||
*/
|
||||
|
||||
#define IPC_INETAVAILABLE 242
|
||||
/* (requires Winamp 2.05+)
|
||||
** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_INETAVAILABLE);
|
||||
**
|
||||
** IPC_INETAVAILABLE will return 1 if the Internet connection is available for Winamp.
|
||||
*/
|
||||
|
||||
#define IPC_UPDTITLE 243
|
||||
/* (requires Winamp 2.2+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_UPDTITLE);
|
||||
**
|
||||
** IPC_UPDTITLE will ask Winamp to update the informations about the current title.
|
||||
*/
|
||||
|
||||
#define IPC_CHANGECURRENTFILE 245
|
||||
/* (requires Winamp 2.05+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)file,IPC_CHANGECURRENTFILE);
|
||||
**
|
||||
** IPC_CHANGECURRENTFILE will set the current playlist item.
|
||||
*/
|
||||
|
||||
#define IPC_GETMBURL 246
|
||||
/* (requires Winamp 2.2+)
|
||||
** char buffer[4096]; // Urls can be VERY long
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)buffer,IPC_GETMBURL);
|
||||
**
|
||||
** IPC_GETMBURL will retrieve the current Minibrowser URL into buffer.
|
||||
*/
|
||||
|
||||
#define IPC_REFRESHPLCACHE 247
|
||||
/* (requires Winamp 2.2+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_REFRESHPLCACHE);
|
||||
**
|
||||
** IPC_REFRESHPLCACHE will flush the playlist cache buffer.
|
||||
*/
|
||||
|
||||
#define IPC_MBBLOCK 248
|
||||
/* (requires Winamp 2.4+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_MBBLOCK);
|
||||
**
|
||||
** IPC_MBBLOCK will block the Minibrowser from updates if value is set to 1
|
||||
*/
|
||||
|
||||
#define IPC_MBOPENREAL 249
|
||||
/* (requires Winamp 2.4+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,(WPARAM)url,IPC_MBOPENREAL);
|
||||
**
|
||||
** IPC_MBOPENREAL works the same as IPC_MBOPEN except that it will works even if
|
||||
** IPC_MBBLOCK has been set to 1
|
||||
*/
|
||||
|
||||
#define IPC_GET_SHUFFLE 250
|
||||
/* (requires Winamp 2.4+)
|
||||
** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_SHUFFLE);
|
||||
**
|
||||
** IPC_GET_SHUFFLE returns the status of the Shuffle option (1 if set)
|
||||
*/
|
||||
|
||||
#define IPC_GET_REPEAT 251
|
||||
/* (requires Winamp 2.4+)
|
||||
** val=SendMessage(hwnd_winamp,WM_WA_IPC,0,IPC_GET_REPEAT);
|
||||
**
|
||||
** IPC_GET_REPEAT returns the status of the Repeat option (1 if set)
|
||||
*/
|
||||
|
||||
#define IPC_SET_SHUFFLE 252
|
||||
/* (requires Winamp 2.4+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_SHUFFLE);
|
||||
**
|
||||
** IPC_SET_SHUFFLE sets the status of the Shuffle option (1 to turn it on)
|
||||
*/
|
||||
|
||||
#define IPC_SET_REPEAT 253
|
||||
/* (requires Winamp 2.4+)
|
||||
** SendMessage(hwnd_winamp,WM_WA_IPC,value,IPC_SET_REPEAT);
|
||||
**
|
||||
** IPC_SET_REPEAT sets the status of the Repeat option (1 to turn it on)
|
||||
*/
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
/*
|
||||
** Some API calls tend to require that you send data via WM_COPYDATA
|
||||
** instead of WM_USER. Such as IPC_PLAYFILE:
|
||||
*/
|
||||
|
||||
#define IPC_PLAYFILE 100
|
||||
|
||||
/*
|
||||
** COPYDATASTRUCT cds;
|
||||
** cds.dwData = IPC_PLAYFILE;
|
||||
** cds.lpData = (void *) "file.mp3";
|
||||
** cds.cbData = strlen((char *) cds.lpData)+1; // include space for null char
|
||||
** SendMessage(hwnd_winamp,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds);
|
||||
**
|
||||
** This will play the file "file.mp3".
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#define IPC_CHDIR 103
|
||||
|
||||
/*
|
||||
** COPYDATASTRUCT cds;
|
||||
** cds.dwData = IPC_CHDIR;
|
||||
** cds.lpData = (void *) "c:\\download";
|
||||
** cds.cbData = strlen((char *) cds.lpData)+1; // include space for null char
|
||||
** SendMessage(hwnd_winamp,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds);
|
||||
**
|
||||
** This will make Winamp change to the directory C:\\download
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
/**************************************************************************/
|
||||
|
||||
/*
|
||||
** Finally there are some WM_COMMAND messages that you can use to send
|
||||
** Winamp misc commands.
|
||||
**
|
||||
** To send these, use:
|
||||
**
|
||||
** SendMessage(hwnd_winamp, WM_COMMAND,command_name,0);
|
||||
*/
|
||||
|
||||
#define WINAMP_OPTIONS_EQ 40036 // toggles the EQ window
|
||||
#define WINAMP_OPTIONS_PLEDIT 40040 // toggles the playlist window
|
||||
#define WINAMP_VOLUMEUP 40058 // turns the volume up a little
|
||||
#define WINAMP_VOLUMEDOWN 40059 // turns the volume down a little
|
||||
#define WINAMP_FFWD5S 40060 // fast forwards 5 seconds
|
||||
#define WINAMP_REW5S 40061 // rewinds 5 seconds
|
||||
|
||||
// the following are the five main control buttons, with optionally shift
|
||||
// or control pressed
|
||||
// (for the exact functions of each, just try it out)
|
||||
#define WINAMP_BUTTON1 40044
|
||||
#define WINAMP_BUTTON2 40045
|
||||
#define WINAMP_BUTTON3 40046
|
||||
#define WINAMP_BUTTON4 40047
|
||||
#define WINAMP_BUTTON5 40048
|
||||
#define WINAMP_BUTTON1_SHIFT 40144
|
||||
#define WINAMP_BUTTON2_SHIFT 40145
|
||||
#define WINAMP_BUTTON3_SHIFT 40146
|
||||
#define WINAMP_BUTTON4_SHIFT 40147
|
||||
#define WINAMP_BUTTON5_SHIFT 40148
|
||||
#define WINAMP_BUTTON1_CTRL 40154
|
||||
#define WINAMP_BUTTON2_CTRL 40155
|
||||
#define WINAMP_BUTTON3_CTRL 40156
|
||||
#define WINAMP_BUTTON4_CTRL 40157
|
||||
#define WINAMP_BUTTON5_CTRL 40158
|
||||
|
||||
#define WINAMP_FILE_PLAY 40029 // pops up the load file(s) box
|
||||
#define WINAMP_OPTIONS_PREFS 40012 // pops up the preferences
|
||||
#define WINAMP_OPTIONS_AOT 40019 // toggles always on top
|
||||
#define WINAMP_HELP_ABOUT 40041 // pops up the about box :)
|
||||
|
||||
|
||||
/*
|
||||
** EOF.. Enjoy.
|
||||
*/
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "out.h"
|
||||
|
||||
/* post this to the main window at end of file (after playback as stopped) */
|
||||
#define WM_WA_MPEG_EOF (WM_USER + 2)
|
||||
|
||||
// note: exported symbol is now winampGetInModule2.
|
||||
|
||||
#define IN_VER 0x100
|
||||
|
||||
290
src/plugin_winamp2/infobox.c
Normal file
290
src/plugin_winamp2/infobox.c
Normal file
@@ -0,0 +1,290 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include "FLAC/all.h"
|
||||
#include "plugin_common/all.h"
|
||||
#include "infobox.h"
|
||||
#include "resource.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char filename[MAX_PATH];
|
||||
} LOCALDATA;
|
||||
|
||||
static char buffer[256];
|
||||
static char *genres = NULL;
|
||||
static int genresSize = 0, genresCount = 0, genresChanged = 0;
|
||||
|
||||
static const char infoTitle[] = "FLAC File Info";
|
||||
|
||||
//fixme int64
|
||||
static __inline DWORD FileSize(const char *file)
|
||||
{
|
||||
HANDLE hFile = CreateFile(file, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
DWORD res;
|
||||
|
||||
if (hFile == INVALID_HANDLE_VALUE) return 0;
|
||||
res = GetFileSize(hFile, 0);
|
||||
CloseHandle(hFile);
|
||||
return res;
|
||||
}
|
||||
|
||||
static __inline int GetGenresFileName(char *buffer, int size)
|
||||
{
|
||||
char *c;
|
||||
|
||||
if (!GetModuleFileName(NULL, buffer, size))
|
||||
return 0;
|
||||
c = strrchr(buffer, '\\');
|
||||
if (!c) return 0;
|
||||
strcpy(c+1, "genres.txt");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void LoadGenres()
|
||||
{
|
||||
HANDLE hFile;
|
||||
DWORD spam;
|
||||
char *c;
|
||||
|
||||
if (!GetGenresFileName(buffer, sizeof(buffer))) return;
|
||||
// load file
|
||||
hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) return;
|
||||
genresSize = GetFileSize(hFile, 0);
|
||||
if (!genresSize) return;
|
||||
genres = (char*)malloc(genresSize+2);
|
||||
if (!genres) return;
|
||||
if (!ReadFile(hFile, genres, genresSize, &spam, NULL))
|
||||
{
|
||||
free(genres);
|
||||
genres = NULL;
|
||||
return;
|
||||
}
|
||||
genres[genresSize] = 0;
|
||||
genres[genresSize+1] = 0;
|
||||
// replace newlines
|
||||
genresChanged = 0;
|
||||
genresCount = 1;
|
||||
|
||||
for (c=genres; *c; c++)
|
||||
{
|
||||
if (*c == 10)
|
||||
{
|
||||
*c = 0;
|
||||
if (*(c+1))
|
||||
genresCount++;
|
||||
else genresSize--;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
static void SaveGenres(HWND hlist)
|
||||
{
|
||||
HANDLE hFile;
|
||||
DWORD spam;
|
||||
int i, count, len;
|
||||
|
||||
if (!GetGenresFileName(buffer, sizeof(buffer))) return;
|
||||
// write file
|
||||
hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE) return;
|
||||
|
||||
count = SendMessage(hlist, CB_GETCOUNT, 0, 0);
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
SendMessage(hlist, CB_GETLBTEXT, i, (LPARAM)buffer);
|
||||
len = strlen(buffer);
|
||||
if (i != count-1)
|
||||
{
|
||||
buffer[len] = 10;
|
||||
len++;
|
||||
}
|
||||
WriteFile(hFile, buffer, len, &spam, NULL);
|
||||
}
|
||||
|
||||
CloseHandle(hFile);
|
||||
}
|
||||
|
||||
|
||||
#define SetText(x,y) SetDlgItemText(hwnd, x, y)
|
||||
#define GetText(x,y) (GetDlgItemText(hwnd, x, buffer, sizeof(buffer)), y = buffer[0] ? strdup(buffer) : 0)
|
||||
|
||||
static BOOL InitInfobox(HWND hwnd, const char *file)
|
||||
{
|
||||
LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));
|
||||
FLAC__StreamMetadata streaminfo;
|
||||
FLAC_Plugin__CanonicalTag tag;
|
||||
DWORD filesize, length, bps, ratio;
|
||||
|
||||
SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);
|
||||
// file name
|
||||
strncpy(data->filename, file, sizeof(data->filename));
|
||||
SetDlgItemText(hwnd, IDC_NAME, file);
|
||||
// stream data
|
||||
filesize = FileSize(file);
|
||||
if (!filesize) return FALSE;
|
||||
if (!FLAC__metadata_get_streaminfo(file, &streaminfo))
|
||||
return FALSE;
|
||||
|
||||
length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);
|
||||
bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));
|
||||
ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);
|
||||
|
||||
sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"
|
||||
"File size: %d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n",
|
||||
streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,
|
||||
streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,
|
||||
length/60, length%60, bps, ratio/10, ratio%10);
|
||||
//todo: replaygain
|
||||
|
||||
SetDlgItemText(hwnd, IDC_INFO, buffer);
|
||||
// tag
|
||||
FLAC_plugin__canonical_tag_init(&tag);
|
||||
FLAC_plugin__canonical_tag_get_combined(file, &tag);
|
||||
|
||||
SetText(IDC_TITLE, tag.title);
|
||||
SetText(IDC_ARTIST, tag.performer ? tag.performer : tag.composer);
|
||||
SetText(IDC_ALBUM, tag.album);
|
||||
SetText(IDC_COMMENT, tag.comment);
|
||||
SetText(IDC_YEAR, tag.year_recorded ? tag.year_recorded : tag.year_performed);
|
||||
SetText(IDC_TRACK, tag.track_number);
|
||||
SetText(IDC_GENRE, tag.genre);
|
||||
|
||||
FLAC_plugin__canonical_tag_clear(&tag);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void __inline SetTag(HWND hwnd, const char *filename, FLAC_Plugin__CanonicalTag *tag)
|
||||
{
|
||||
strcpy(buffer, infoTitle);
|
||||
|
||||
if (FLAC_plugin__vorbiscomment_set(filename, tag))
|
||||
strcat(buffer, " [Updated]");
|
||||
else strcat(buffer, " [Failed]");
|
||||
|
||||
SetWindowText(hwnd, buffer);
|
||||
}
|
||||
|
||||
static void UpdateTag(HWND hwnd)
|
||||
{
|
||||
LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
|
||||
FLAC_Plugin__CanonicalTag tag;
|
||||
FLAC_plugin__canonical_tag_init(&tag);
|
||||
|
||||
// get fields
|
||||
GetText(IDC_TITLE, tag.title);
|
||||
GetText(IDC_ARTIST, tag.composer);
|
||||
GetText(IDC_ALBUM, tag.album);
|
||||
GetText(IDC_COMMENT, tag.comment);
|
||||
GetText(IDC_YEAR, tag.year_recorded);
|
||||
GetText(IDC_TRACK, tag.track_number);
|
||||
GetText(IDC_GENRE, tag.genre);
|
||||
|
||||
// update genres list
|
||||
if (tag.genre)
|
||||
{
|
||||
HWND hgen = GetDlgItem(hwnd, IDC_GENRE);
|
||||
|
||||
if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)tag.genre) == CB_ERR)
|
||||
{
|
||||
genresChanged = 1;
|
||||
SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)tag.genre);
|
||||
}
|
||||
}
|
||||
|
||||
// write tag
|
||||
SetTag(hwnd, data->filename, &tag);
|
||||
FLAC_plugin__canonical_tag_clear(&tag);
|
||||
}
|
||||
|
||||
static void RemoveTag(HWND hwnd)
|
||||
{
|
||||
LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);
|
||||
FLAC_Plugin__CanonicalTag tag;
|
||||
FLAC_plugin__canonical_tag_init(&tag);
|
||||
|
||||
SetText(IDC_TITLE, "");
|
||||
SetText(IDC_ARTIST, "");
|
||||
SetText(IDC_ALBUM, "");
|
||||
SetText(IDC_COMMENT, "");
|
||||
SetText(IDC_YEAR, "");
|
||||
SetText(IDC_TRACK, "");
|
||||
SetText(IDC_GENRE, "");
|
||||
|
||||
SetTag(hwnd, data->filename, &tag);
|
||||
}
|
||||
|
||||
|
||||
static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg)
|
||||
{
|
||||
// init
|
||||
case WM_INITDIALOG:
|
||||
SetWindowText(hwnd, infoTitle);
|
||||
// init genres list
|
||||
{
|
||||
HWND hgen = GetDlgItem(hwnd, IDC_GENRE);
|
||||
char *c;
|
||||
|
||||
// set text length limit to 64 chars
|
||||
SendMessage(hgen, CB_LIMITTEXT, 64, 0);
|
||||
// try to load genres
|
||||
if (!genres) LoadGenres(hgen);
|
||||
// add the to list
|
||||
if (genres)
|
||||
{
|
||||
SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize);
|
||||
|
||||
for (c = genres; *c; c += strlen(c)+1)
|
||||
SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c);
|
||||
}
|
||||
}
|
||||
// init fields
|
||||
if (!InitInfobox(hwnd, (const char*)lParam))
|
||||
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
||||
return TRUE;
|
||||
// destroy
|
||||
case WM_DESTROY:
|
||||
if (genresChanged)
|
||||
{
|
||||
SaveGenres(GetDlgItem(hwnd, IDC_GENRE));
|
||||
free(genres);
|
||||
genres = 0;
|
||||
}
|
||||
LocalFree((LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA));
|
||||
break;
|
||||
// commands
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
// ok/cancel
|
||||
case IDOK:
|
||||
case IDCANCEL:
|
||||
EndDialog(hwnd, LOWORD(wParam));
|
||||
return TRUE;
|
||||
// save
|
||||
case IDC_UPDATE:
|
||||
UpdateTag(hwnd);
|
||||
break;
|
||||
// remove
|
||||
case IDC_REMOVE:
|
||||
RemoveTag(hwnd);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename)
|
||||
{
|
||||
DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);
|
||||
}
|
||||
5
src/plugin_winamp2/infobox.h
Normal file
5
src/plugin_winamp2/infobox.h
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// prototypes
|
||||
//
|
||||
|
||||
void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename);
|
||||
@@ -4,23 +4,38 @@
|
||||
//
|
||||
#define IDC_RESET 3
|
||||
#define IDD_CONFIG 101
|
||||
#define IDD_CONFIG_GENERAL 103
|
||||
#define IDD_CONFIG_OUTPUT 104
|
||||
#define IDD_INFOBOX 105
|
||||
#define IDC_ENABLE 1000
|
||||
#define IDC_ALBUM 1001
|
||||
#define IDC_LIMITER 1002
|
||||
#define IDC_COMMENT 1002
|
||||
#define IDC_PREAMP 1003
|
||||
#define IDC_YEAR 1003
|
||||
#define IDC_PA 1004
|
||||
#define IDC_TRACK 1004
|
||||
#define IDC_DITHER 1005
|
||||
#define IDC_DITHERRG 1006
|
||||
#define IDC_TO 1008
|
||||
#define IDC_SHAPE 1009
|
||||
#define IDC_TABS 1009
|
||||
#define IDC_TITLE 1010
|
||||
#define IDC_TAGZ_HELP 1011
|
||||
#define IDC_ARTIST 1011
|
||||
#define IDC_NAME 1014
|
||||
#define IDC_INFO 1015
|
||||
#define IDC_GENRE 1017
|
||||
#define IDC_REMOVE 1020
|
||||
#define IDC_UPDATE 1021
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 102
|
||||
#define _APS_NEXT_RESOURCE_VALUE 106
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1009
|
||||
#define _APS_NEXT_CONTROL_VALUE 1030
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -21,67 +21,6 @@ LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
|
||||
#pragma code_page(1251)
|
||||
#endif //_WIN32
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_CONFIG DIALOG DISCARDABLE 0, 0, 227, 193
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "FLAC Configuration"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
GROUPBOX " ReplayGain ",IDC_STATIC,4,2,220,57
|
||||
CONTROL "&Enable ReplayGain",IDC_ENABLE,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,10,15,77,10
|
||||
CONTROL "&Album mode",IDC_ALBUM,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,10,27,55,10
|
||||
CONTROL "6dB &hard limiter",IDC_LIMITER,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,125,27,64,10
|
||||
LTEXT "&Preamp",IDC_STATIC,10,44,25,8
|
||||
CONTROL "Slider1",IDC_PREAMP,"msctls_trackbar32",TBS_NOTICKS |
|
||||
WS_TABSTOP,38,43,154,12
|
||||
RTEXT "",IDC_PA,196,44,21,8
|
||||
GROUPBOX " Resolution ",IDC_STATIC,3,62,220,96
|
||||
GROUPBOX " Without ReplayGain ",IDC_STATIC,9,71,209,30
|
||||
CONTROL "&Dither 24bps to 16bps",IDC_DITHER,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,18,84,85,10
|
||||
GROUPBOX " With ReplayGain ",IDC_STATIC,9,104,209,48
|
||||
CONTROL "E&nable dithering",IDC_DITHERRG,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,18,116,67,10
|
||||
LTEXT "Dither &to",IDC_STATIC,18,135,28,8
|
||||
COMBOBOX IDC_TO,52,132,39,43,CBS_DROPDOWNLIST | WS_VSCROLL |
|
||||
WS_TABSTOP
|
||||
LTEXT "Noise &shaping",IDC_STATIC,115,135,46,8
|
||||
COMBOBOX IDC_SHAPE,166,132,46,48,CBS_DROPDOWNLIST | WS_VSCROLL |
|
||||
WS_TABSTOP
|
||||
DEFPUSHBUTTON "OK",IDOK,65,176,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,119,176,50,14
|
||||
PUSHBUTTON "Reset",IDC_RESET,173,176,50,14
|
||||
LTEXT "Note: pressing OK will stop FLAC playback",IDC_STATIC,4,
|
||||
162,135,8
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
IDD_CONFIG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 220
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 186
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -107,10 +46,170 @@ END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_INFOBOX DIALOG DISCARDABLE 0, 0, 292, 148
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
EDITTEXT IDC_NAME,3,3,285,12,ES_AUTOHSCROLL | ES_READONLY | NOT
|
||||
WS_TABSTOP
|
||||
GROUPBOX " Tag ",IDC_STATIC,3,20,182,104
|
||||
RTEXT "&Title",IDC_STATIC,8,32,31,8
|
||||
EDITTEXT IDC_TITLE,43,30,137,12,ES_AUTOHSCROLL
|
||||
RTEXT "&Artist",IDC_STATIC,8,47,31,8
|
||||
EDITTEXT IDC_ARTIST,43,45,137,12,ES_AUTOHSCROLL
|
||||
RTEXT "Albu&m",IDC_STATIC,8,62,31,8
|
||||
EDITTEXT IDC_ALBUM,43,60,137,12,ES_AUTOHSCROLL
|
||||
RTEXT "&Comment",IDC_STATIC,8,77,31,8
|
||||
EDITTEXT IDC_COMMENT,43,75,137,12,ES_AUTOHSCROLL
|
||||
RTEXT "&Year",IDC_STATIC,8,92,31,8
|
||||
EDITTEXT IDC_YEAR,43,90,40,12,ES_AUTOHSCROLL
|
||||
RTEXT "Track &number",IDC_STATIC,90,92,46,8
|
||||
EDITTEXT IDC_TRACK,141,90,39,12,ES_AUTOHSCROLL
|
||||
RTEXT "&Genre",IDC_STATIC,8,107,31,8
|
||||
COMBOBOX IDC_GENRE,43,105,137,95,CBS_DROPDOWN | CBS_SORT |
|
||||
WS_VSCROLL | WS_TABSTOP
|
||||
GROUPBOX " FLAC Info ",IDC_STATIC,191,20,97,124
|
||||
LTEXT "",IDC_INFO,195,30,90,110
|
||||
DEFPUSHBUTTON "Close",IDOK,3,130,50,14
|
||||
PUSHBUTTON "&Update",IDC_UPDATE,69,130,50,14
|
||||
PUSHBUTTON "&Remove",IDC_REMOVE,135,130,50,14
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
IDD_INFOBOX, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 285
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 141
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // Russian resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dialog
|
||||
//
|
||||
|
||||
IDD_CONFIG DIALOG DISCARDABLE 0, 0, 237, 212
|
||||
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||
CAPTION "FLAC Configuration"
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
DEFPUSHBUTTON "OK",IDOK,75,195,50,14
|
||||
PUSHBUTTON "Cancel",IDCANCEL,129,195,50,14
|
||||
PUSHBUTTON "Reset",IDC_RESET,183,195,50,14
|
||||
CONTROL "Tab1",IDC_TABS,"SysTabControl32",WS_TABSTOP,3,3,230,187
|
||||
END
|
||||
|
||||
IDD_CONFIG_GENERAL DIALOG DISCARDABLE 0, 0, 226, 171
|
||||
STYLE DS_CONTROL | WS_CHILD
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
GROUPBOX " Title Formatting ",IDC_STATIC,2,2,220,43
|
||||
LTEXT "&Title:",IDC_STATIC,8,17,16,8
|
||||
EDITTEXT IDC_TITLE,27,15,188,12,ES_AUTOHSCROLL
|
||||
PUSHBUTTON "help",IDC_TAGZ_HELP,188,28,27,10
|
||||
END
|
||||
|
||||
IDD_CONFIG_OUTPUT DIALOG DISCARDABLE 0, 0, 224, 171
|
||||
STYLE DS_CONTROL | WS_CHILD
|
||||
FONT 8, "MS Sans Serif"
|
||||
BEGIN
|
||||
GROUPBOX " ReplayGain ",IDC_STATIC,2,2,220,57
|
||||
CONTROL "&Enable ReplayGain",IDC_ENABLE,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,8,15,77,10
|
||||
CONTROL "&Album mode",IDC_ALBUM,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,8,27,55,10
|
||||
CONTROL "6dB &hard limiter",IDC_LIMITER,"Button",BS_AUTOCHECKBOX |
|
||||
WS_TABSTOP,123,27,64,10
|
||||
LTEXT "&Preamp",IDC_STATIC,8,44,25,8
|
||||
CONTROL "Slider1",IDC_PREAMP,"msctls_trackbar32",TBS_NOTICKS |
|
||||
WS_TABSTOP,36,43,154,12
|
||||
RTEXT "",IDC_PA,194,44,21,8
|
||||
GROUPBOX " Resolution ",IDC_STATIC,1,62,220,95
|
||||
GROUPBOX " Without ReplayGain ",IDC_STATIC,7,71,209,30
|
||||
CONTROL "&Dither 24bps to 16bps",IDC_DITHER,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,16,84,85,10
|
||||
GROUPBOX " With ReplayGain ",IDC_STATIC,7,104,209,47
|
||||
CONTROL "E&nable dithering",IDC_DITHERRG,"Button",
|
||||
BS_AUTOCHECKBOX | WS_TABSTOP,16,134,67,10
|
||||
LTEXT "&Output bit depth",IDC_STATIC,16,119,52,8
|
||||
COMBOBOX IDC_TO,71,116,39,43,CBS_DROPDOWNLIST | WS_VSCROLL |
|
||||
WS_TABSTOP
|
||||
LTEXT "Noise &shaping",IDC_STATIC,113,135,46,8
|
||||
COMBOBOX IDC_SHAPE,164,132,46,48,CBS_DROPDOWNLIST | WS_VSCROLL |
|
||||
WS_TABSTOP
|
||||
LTEXT "Note: changes take effect after restarting playback",
|
||||
IDC_STATIC,2,160,161,8
|
||||
END
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// DESIGNINFO
|
||||
//
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
GUIDELINES DESIGNINFO DISCARDABLE
|
||||
BEGIN
|
||||
IDD_CONFIG, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 230
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 205
|
||||
END
|
||||
|
||||
IDD_CONFIG_GENERAL, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 219
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 164
|
||||
END
|
||||
|
||||
IDD_CONFIG_OUTPUT, DIALOG
|
||||
BEGIN
|
||||
LEFTMARGIN, 7
|
||||
RIGHTMARGIN, 217
|
||||
TOPMARGIN, 7
|
||||
BOTTOMMARGIN, 164
|
||||
END
|
||||
END
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
923
src/plugin_winamp2/tagz.cpp
Normal file
923
src/plugin_winamp2/tagz.cpp
Normal file
@@ -0,0 +1,923 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include "tagz.h"
|
||||
|
||||
#ifdef TAGZ_UNICODE
|
||||
|
||||
#define _TX(X) L##X
|
||||
#define t_strdup wcsdup
|
||||
#define t_strlen wcslen
|
||||
#define t_strnicmp wcsnicmp
|
||||
#define t_atoi(x) wcstol(x,0,10)
|
||||
#define t_stricmp wcsicmp
|
||||
#define t_strstr wcsstr
|
||||
#define sprintf swprintf
|
||||
|
||||
#else
|
||||
|
||||
#define _TX(X) X
|
||||
#define t_strdup strdup
|
||||
#define t_strlen strlen
|
||||
#define t_strnicmp strnicmp
|
||||
#define t_atoi atoi
|
||||
#define t_stricmp stricmp
|
||||
#define t_strstr strstr
|
||||
|
||||
#endif
|
||||
|
||||
#define TABSIZE(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
|
||||
class T_String
|
||||
{
|
||||
private:
|
||||
T_CHAR * data;
|
||||
UINT size,used;
|
||||
public:
|
||||
T_String() {data=0;size=0;used=0;}
|
||||
void AddChar(T_CHAR c)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
data=(T_CHAR*)malloc((size=512)*sizeof(T_CHAR));
|
||||
used=0;
|
||||
}
|
||||
else if (size==used)
|
||||
{
|
||||
size<<=1;
|
||||
data=(T_CHAR*)realloc((char*)data,size*sizeof(T_CHAR));
|
||||
}
|
||||
if (data) data[used++]=c;
|
||||
}
|
||||
void AddInt(int i)
|
||||
{
|
||||
T_CHAR foo[16];
|
||||
sprintf(foo,_TX("%i"),i);
|
||||
AddString(foo);
|
||||
}
|
||||
void AddString(const T_CHAR * z)
|
||||
{
|
||||
while(*z) {AddChar(*z);z++;}
|
||||
}
|
||||
void AddString(T_String & s)
|
||||
{
|
||||
AddString(s.Peek());
|
||||
}
|
||||
~T_String()
|
||||
{
|
||||
if (data) free(data);
|
||||
}
|
||||
T_CHAR * GetBuf()
|
||||
{
|
||||
if (!data) return ::t_strdup(_TX(""));
|
||||
T_CHAR * r=(T_CHAR*)realloc(data,(used+1)*sizeof(T_CHAR));
|
||||
r[used]=0;
|
||||
data=0;
|
||||
return r;
|
||||
}
|
||||
T_CHAR operator[](UINT i)
|
||||
{
|
||||
if (!data || i>=used) return 0;
|
||||
else return data[i];
|
||||
}
|
||||
UINT Len() {return data ? used : 0;}
|
||||
void Reset()
|
||||
{
|
||||
if (data) {free(data);data=0;}
|
||||
}
|
||||
const T_CHAR * Peek()
|
||||
{
|
||||
AddChar(0);
|
||||
used--;
|
||||
return data;
|
||||
}
|
||||
T_CHAR * strdup()
|
||||
{
|
||||
return ::t_strdup(Peek());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
static int separator(T_CHAR x)
|
||||
{
|
||||
if (!x || x==' ') return 1;
|
||||
if (x=='\'' || x=='_') return 0;
|
||||
#ifdef TAGZ_UNICODE
|
||||
return !iswalnum(x);
|
||||
#else
|
||||
return !isalnum(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sepcmp(T_CHAR* src,T_CHAR* val)
|
||||
{
|
||||
UINT l=t_strlen(val);
|
||||
return !t_strnicmp(src,val,l) && separator(src[l]);
|
||||
}
|
||||
|
||||
static char roman_num[]=
|
||||
{
|
||||
'I','V','X','L','C','D','M'
|
||||
};
|
||||
|
||||
|
||||
static int is_roman(T_CHAR * ptr)//could be more smart i think
|
||||
{
|
||||
if (ptr[0]==']' && ptr[1]=='[' && separator(ptr[2])) return 1;
|
||||
while(!separator(*ptr))
|
||||
{
|
||||
UINT n;
|
||||
bool found=0;
|
||||
for(n=0;n<TABSIZE(roman_num);n++)
|
||||
{
|
||||
if (*ptr==roman_num[n]) {found=1;break;}
|
||||
}
|
||||
if (!found) return 0;
|
||||
ptr++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int need_full(T_CHAR* ptr)
|
||||
{
|
||||
if (is_roman(ptr)) return 1;
|
||||
if (sepcmp(ptr,_TX("RPG"))) return 1;
|
||||
while(!separator(*ptr))
|
||||
{
|
||||
if (*ptr<'0' || *ptr>'9') return 0;
|
||||
ptr++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef bool (*TEXTFUNC)(UINT n_src,T_CHAR **src,UINT*,T_String &out);
|
||||
|
||||
#define MAKEFUNC(X) static bool X(UINT n_src,T_CHAR ** src,UINT *found_src,T_String &out)
|
||||
|
||||
|
||||
MAKEFUNC(If)
|
||||
{
|
||||
if (n_src!=3) return false;
|
||||
|
||||
out.AddString(src[found_src[0] ? 1 : 2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(If2)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
out.AddString(src[found_src[0] ? 0 : 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
MAKEFUNC(Iflonger)
|
||||
{
|
||||
if (n_src!=4) return false;
|
||||
|
||||
out.AddString(src[(int)t_strlen(src[0])>t_atoi(src[1]) ? 2 : 3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Ifgreater)
|
||||
{
|
||||
if (n_src!=4) return false;
|
||||
|
||||
out.AddString(src[t_atoi(src[0])>t_atoi(src[1]) ? 2 : 3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Upper)
|
||||
{
|
||||
if (n_src!=1) return false;
|
||||
|
||||
T_CHAR * s=src[0];
|
||||
|
||||
while(*s)
|
||||
out.AddChar(toupper(*(s++)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Lower)
|
||||
{
|
||||
if (n_src!=1) return false;
|
||||
|
||||
T_CHAR * s=src[0];
|
||||
|
||||
while(*s)
|
||||
out.AddChar(tolower(*(s++)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Pad)
|
||||
{
|
||||
if (n_src<2 || n_src>3) return false;
|
||||
|
||||
T_CHAR *fill=_TX(" ");
|
||||
if (n_src==3 && src[2][0])
|
||||
fill = src[2];
|
||||
|
||||
int num = t_atoi(src[1]);
|
||||
T_CHAR *p = src[0];
|
||||
|
||||
while (*p) { out.AddChar(*(p++)); num--; }
|
||||
|
||||
UINT fl = t_strlen(fill);
|
||||
while (num>0)
|
||||
out.AddChar(fill[(--num)%fl]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Cut)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
UINT num = t_atoi(src[1]);
|
||||
T_CHAR *p = src[0];
|
||||
|
||||
while (*p && num>0) {out.AddChar(*(p++));num--;}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(PadCut)
|
||||
{
|
||||
if (n_src<2 || n_src>3) return false;
|
||||
|
||||
T_CHAR *fill = _TX(" ");
|
||||
if (n_src==3 && src[2][0])
|
||||
fill = src[2];
|
||||
|
||||
int num = t_atoi(src[1]);
|
||||
T_CHAR *p = src[0];
|
||||
|
||||
while(*p && num>0) {out.AddChar(*(p++));num--;}
|
||||
|
||||
UINT fl=t_strlen(fill);
|
||||
while (num>0)
|
||||
out.AddChar(fill[(--num)%fl]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// abbr(string)
|
||||
// abbr(string,len)
|
||||
MAKEFUNC(Abbr)
|
||||
{
|
||||
if (n_src==0 || n_src>2) return false;
|
||||
|
||||
|
||||
if (n_src==2 && (int)t_strlen(src[0])<t_atoi(src[1]))
|
||||
{
|
||||
out.AddString(src[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
T_CHAR * meta=src[0];
|
||||
bool w=0, r=0;
|
||||
|
||||
while(*meta)
|
||||
{
|
||||
bool an=!separator(*meta) || *meta==']' || *meta=='[';
|
||||
|
||||
if (w && !an)
|
||||
w=0;
|
||||
else if (!w && an)
|
||||
{
|
||||
w=1;
|
||||
r=need_full(meta)?1:0;
|
||||
out.AddChar(*meta);
|
||||
}
|
||||
else if (w && r)
|
||||
out.AddChar(*meta);
|
||||
meta++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MAKEFUNC(Caps)
|
||||
{
|
||||
if (n_src!=1) return false;
|
||||
|
||||
T_CHAR* sp=src[0];
|
||||
int sep = 1;
|
||||
|
||||
while(*sp)
|
||||
{
|
||||
T_CHAR c=*(sp++);
|
||||
int s = separator(c);
|
||||
if (!s && sep)
|
||||
c=toupper(c);
|
||||
else if (!sep) c=tolower(c);
|
||||
sep=s;
|
||||
out.AddChar(c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Caps2)
|
||||
{
|
||||
if (n_src!=1) return false;
|
||||
|
||||
T_CHAR* sp=src[0];
|
||||
int sep=1;
|
||||
|
||||
while(*sp)
|
||||
{
|
||||
T_CHAR c=*(sp++);
|
||||
int s = separator(c);
|
||||
if (!s && sep)
|
||||
c=toupper(c);
|
||||
sep=s;
|
||||
out.AddChar(c);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Longest)
|
||||
{
|
||||
T_CHAR *ptr=0;
|
||||
UINT n, m=0;
|
||||
|
||||
for(n=0;n<n_src;n++)
|
||||
{
|
||||
UINT l=t_strlen(src[n]);
|
||||
if (l>m) {m=l;ptr=src[n];}
|
||||
}
|
||||
|
||||
if (ptr) out.AddString(ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Shortest)
|
||||
{
|
||||
T_CHAR * ptr=0;
|
||||
UINT n,m=(UINT)(-1);
|
||||
|
||||
for(n=0;n<n_src;n++)
|
||||
{
|
||||
UINT l=t_strlen(src[n]);
|
||||
if (l<m) {m=l;ptr=src[n];}
|
||||
}
|
||||
|
||||
if (ptr) out.AddString(ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Num)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
T_CHAR tmp[16];
|
||||
T_CHAR tmp1[16];
|
||||
sprintf(tmp1,_TX("%%0%uu"),t_atoi(src[1]));
|
||||
sprintf(tmp,tmp1,t_atoi(src[0]));
|
||||
out.AddString(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Hex)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
T_CHAR tmp[16];
|
||||
T_CHAR tmp1[16];
|
||||
sprintf(tmp1,_TX("%%0%ux"),t_atoi(src[1]));
|
||||
sprintf(tmp,tmp1,t_atoi(src[0]));
|
||||
out.AddString(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(StrChr)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
T_CHAR * p=src[0];
|
||||
T_CHAR s=src[1][0];
|
||||
|
||||
while (*p && *p!=s) p++;
|
||||
if (*p==s)
|
||||
out.AddInt(1+p-src[0]);
|
||||
else out.AddChar('0');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(StrRChr)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
T_CHAR * p=src[0],*p1=0;
|
||||
T_CHAR s=src[1][0];
|
||||
|
||||
while(*p)
|
||||
{
|
||||
if (*p==s) p1=p;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p1)
|
||||
out.AddInt(1+p1-src[0]);
|
||||
else out.AddChar('0');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(StrStr)
|
||||
{
|
||||
if (n_src!=2) return false;
|
||||
|
||||
T_CHAR * p = t_strstr(src[0],src[1]);
|
||||
|
||||
if (p)
|
||||
out.AddInt(1+p-src[0]);
|
||||
else out.AddChar('0');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// substr(string, index)
|
||||
// substr(string, index, length)
|
||||
MAKEFUNC(SubStr)
|
||||
{
|
||||
if (n_src<2 || n_src>3) return false;
|
||||
|
||||
int n1 = t_atoi(src[1]), n2;
|
||||
|
||||
if (n_src == 3)
|
||||
n2 = t_atoi(src[2]);
|
||||
else n2 = n1;
|
||||
|
||||
if (n1 < 1) n1=1;
|
||||
if (n2 >= n1)
|
||||
{
|
||||
n1--;
|
||||
n2--;
|
||||
while(n1<=n2 && src[0][n1])
|
||||
out.AddChar(src[0][n1++]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Len)
|
||||
{
|
||||
if (n_src!=1) return false;
|
||||
|
||||
out.AddInt(t_strlen(src[0]));
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Add)
|
||||
{
|
||||
UINT n;
|
||||
int s=0;
|
||||
|
||||
for (n=0;n<n_src;n++)
|
||||
s+=t_atoi(src[n]);
|
||||
|
||||
out.AddInt(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Sub)
|
||||
{
|
||||
if (n_src==0) return false;
|
||||
|
||||
UINT n;
|
||||
int s=t_atoi(src[0]);
|
||||
|
||||
for (n=1;n<n_src;n++)
|
||||
s-=t_atoi(src[n]);
|
||||
|
||||
out.AddInt(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Mul)
|
||||
{
|
||||
UINT n;
|
||||
int s=1;
|
||||
|
||||
for(n=0;n<n_src;n++)
|
||||
s*=t_atoi(src[n]);
|
||||
|
||||
out.AddInt(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Div)
|
||||
{
|
||||
if (n_src==0) return false;
|
||||
|
||||
UINT n;
|
||||
int s=t_atoi(src[0]);
|
||||
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t) s/=t;
|
||||
else t=0;
|
||||
}
|
||||
|
||||
out.AddInt(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Mod)
|
||||
{
|
||||
if (n_src==0) return false;
|
||||
|
||||
UINT n;
|
||||
int s=t_atoi(src[0]);
|
||||
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t) s%=t;
|
||||
else t=0;
|
||||
}
|
||||
|
||||
out.AddInt(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Max)
|
||||
{
|
||||
if (!n_src) return false;
|
||||
|
||||
int m = t_atoi(src[0]);
|
||||
UINT n;
|
||||
|
||||
for (n=1; n<n_src; n++)
|
||||
{
|
||||
int t = t_atoi(src[n]);
|
||||
if (t > m) m = t;
|
||||
}
|
||||
out.AddInt(m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MAKEFUNC(Min)
|
||||
{
|
||||
if (!n_src) return false;
|
||||
|
||||
int m=t_atoi(src[0]);
|
||||
UINT n;
|
||||
|
||||
for(n=1;n<n_src;n++)
|
||||
{
|
||||
int t=t_atoi(src[n]);
|
||||
if (t<m) m=t;
|
||||
}
|
||||
out.AddInt(m);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// replace(string, what_to_replace, replacement)
|
||||
MAKEFUNC(Replace)
|
||||
{
|
||||
if (n_src!=3) return false;
|
||||
T_CHAR *p = src[0];
|
||||
|
||||
while (*p)
|
||||
{
|
||||
UINT n=0;
|
||||
|
||||
while (src[1][n] && p[n]==src[1][n]) n++;
|
||||
|
||||
if (!src[1][n])
|
||||
{
|
||||
out.AddString(src[2]);
|
||||
p += n;
|
||||
}
|
||||
else out.AddChar(*p++);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
TEXTFUNC func;
|
||||
const T_CHAR * name;
|
||||
}
|
||||
FUNCS[] =
|
||||
{
|
||||
If,_TX("if"),
|
||||
If2,_TX("if2"),
|
||||
Upper,_TX("upper"),
|
||||
Lower,_TX("lower"),
|
||||
Pad,_TX("pad"),
|
||||
Cut,_TX("cut"),
|
||||
PadCut,_TX("padcut"),
|
||||
Abbr,_TX("abbr"),
|
||||
Caps,_TX("caps"),
|
||||
Caps2,_TX("caps2"),
|
||||
Longest,_TX("longest"),
|
||||
Shortest,_TX("shortest"),
|
||||
Iflonger,_TX("iflonger"),
|
||||
Ifgreater,_TX("ifgreater"),
|
||||
Num,_TX("num"),Num,_TX("dec"),
|
||||
Hex,_TX("hex"),
|
||||
StrChr,_TX("strchr"),
|
||||
StrChr,_TX("strlchr"),
|
||||
StrRChr,_TX("strrchr"),
|
||||
StrStr,_TX("strstr"),
|
||||
SubStr,_TX("substr"),
|
||||
Len,_TX("len"),
|
||||
Add,_TX("add"),
|
||||
Sub,_TX("sub"),
|
||||
Mul,_TX("mul"),
|
||||
Div,_TX("div"),
|
||||
Mod,_TX("mod"),
|
||||
Min,_TX("min"),
|
||||
Max,_TX("max"),
|
||||
Replace,_TX("replace"),
|
||||
};
|
||||
|
||||
|
||||
class FMT
|
||||
{
|
||||
private:
|
||||
T_String str;
|
||||
T_CHAR * spec;
|
||||
TAGFUNC f;
|
||||
TAGFREEFUNC ff;
|
||||
void * fp;
|
||||
T_CHAR * org_spec;
|
||||
int found;
|
||||
|
||||
void Error(T_CHAR *e=0)
|
||||
{
|
||||
str.Reset();
|
||||
str.AddString(e ? e : _TX("[SYNTAX ERROR IN FORMATTING STRING]"));
|
||||
found++; // force displaying
|
||||
}
|
||||
|
||||
T_CHAR * _FMT(T_CHAR * s,UINT *f=0)
|
||||
{
|
||||
FMT fmt(this,s);
|
||||
T_CHAR * c=(T_CHAR*)fmt;
|
||||
if (f) *f=fmt.found;
|
||||
found+=fmt.found;
|
||||
return c;
|
||||
}
|
||||
|
||||
static bool skipshit(T_CHAR** _p,T_CHAR *bl)
|
||||
{
|
||||
T_CHAR * p=*_p;
|
||||
int bc1=0,bc2=0;
|
||||
while(*p)
|
||||
{
|
||||
if (!bc1 && !bc2 && bl)
|
||||
{
|
||||
T_CHAR *z=bl;
|
||||
while(*z)
|
||||
{
|
||||
if (*z==*p) break;
|
||||
z++;
|
||||
}
|
||||
if (*z) break;
|
||||
}
|
||||
if (*p=='\'')
|
||||
{
|
||||
p++;
|
||||
while(*p && *p!='\'') p++;
|
||||
if (!*p) return 0;
|
||||
}
|
||||
else if (*p=='(') bc1++;
|
||||
else if (*p==')')
|
||||
{
|
||||
if (--bc1<0) return 0;
|
||||
}
|
||||
else if (*p=='[') bc2++;
|
||||
else if (*p==']')
|
||||
{
|
||||
if (--bc2<0) return 0;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
*_p=p;
|
||||
return *p && !bc1 && !bc2;
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
if (!spec) {Error();return;}
|
||||
while(*spec)
|
||||
{
|
||||
if (*spec=='%')
|
||||
{
|
||||
spec++;
|
||||
if (*spec=='%') {str.AddChar('%');spec++;continue;}
|
||||
T_CHAR* s1=spec+1;
|
||||
while(*s1 && *s1!='%') s1++;
|
||||
if (!*s1) {Error();break;}
|
||||
*s1=0;
|
||||
const T_CHAR * tag=f(spec,fp);
|
||||
*s1='%';
|
||||
//if (!tag) tag=tag_unknown;
|
||||
if (tag && tag[0])
|
||||
{
|
||||
found++;
|
||||
str.AddString(tag);
|
||||
}
|
||||
else
|
||||
{
|
||||
str.AddString(_TX("?"));
|
||||
}
|
||||
if (tag && ff) ff(tag,fp);
|
||||
spec=s1+1;
|
||||
}
|
||||
else if (*spec=='$')
|
||||
{
|
||||
spec++;
|
||||
if (*spec=='$') {str.AddChar('$');spec++;continue;}
|
||||
T_CHAR * s1=spec+1;
|
||||
while(*s1 && *s1!='(') s1++;
|
||||
if (!*s1) {Error();break;}
|
||||
T_CHAR * s2=s1+1;
|
||||
if (!skipshit(&s2,_TX(")"))) {Error();break;}
|
||||
if (!*s2) {Error();break;};
|
||||
T_CHAR * p=s1+1;
|
||||
T_CHAR* temp[64];
|
||||
UINT temp_f[64];
|
||||
UINT nt=0;
|
||||
T_CHAR * p1=s1+1;
|
||||
while(p<=s2 && nt<64)
|
||||
{
|
||||
if (!skipshit(&p,_TX(",)"))) {Error();return;}
|
||||
if (p>s2 || (*p!=',' && *p!=')')) {Error(_TX("internal error"));return;}
|
||||
T_CHAR bk=*p;
|
||||
*p=0;
|
||||
temp[nt]=_FMT(p1,&temp_f[nt]);
|
||||
nt++;
|
||||
*p=bk;;
|
||||
p1=p+1;
|
||||
p++;
|
||||
}
|
||||
*s1=0;
|
||||
UINT n;
|
||||
|
||||
for (n=0; n<TABSIZE(FUNCS); n++)
|
||||
if (!t_stricmp(spec, FUNCS[n].name))
|
||||
break;
|
||||
|
||||
*s1='(';
|
||||
|
||||
if (n != TABSIZE(FUNCS))
|
||||
{
|
||||
if (!FUNCS[n].func(nt, temp, temp_f, str))
|
||||
{
|
||||
Error(_TX("[INVALID $"));
|
||||
str.AddString(FUNCS[n].name);
|
||||
str.AddString(_TX(" SYNTAX]"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error(_TX("[UNKNOWN FUNCTION]"));
|
||||
return;
|
||||
}
|
||||
|
||||
for(n=0;n<nt;n++) free(temp[n]);
|
||||
spec=s2+1;
|
||||
}
|
||||
else if (*spec=='\'')
|
||||
{
|
||||
spec++;
|
||||
if (*spec=='\'') {str.AddChar('\'');spec++;continue;}
|
||||
T_CHAR * s1=spec+1;
|
||||
while(*s1 && *s1!='\'') s1++;
|
||||
if (!*s1) {Error();break;}
|
||||
*s1=0;
|
||||
str.AddString(spec);
|
||||
*s1='\'';
|
||||
spec=s1+1;
|
||||
}
|
||||
else if (*spec=='[')
|
||||
{
|
||||
spec++;
|
||||
T_CHAR * s1=spec;
|
||||
UINT bc=0;
|
||||
if (!skipshit(&s1,_TX("]"))) {Error();break;}
|
||||
T_CHAR bk=*s1;
|
||||
*s1=0;
|
||||
FMT fmt(this,spec);
|
||||
fmt.run();
|
||||
if (fmt.found)
|
||||
{
|
||||
str.AddString(fmt.str);
|
||||
found+=fmt.found;
|
||||
}
|
||||
*s1=bk;
|
||||
spec=s1+1;
|
||||
}
|
||||
else if (*spec == ']') {Error();break;}
|
||||
else
|
||||
{
|
||||
str.AddChar(*spec);
|
||||
spec++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FMT(FMT* base,T_CHAR * _spec)
|
||||
{
|
||||
found=0;
|
||||
org_spec=0;
|
||||
f=base->f;
|
||||
ff=base->ff;
|
||||
fp=base->fp;
|
||||
spec=_spec;
|
||||
}
|
||||
public:
|
||||
FMT(const T_CHAR * p_spec,TAGFUNC _f,TAGFREEFUNC _ff,void * _fp)
|
||||
{
|
||||
found=0;
|
||||
org_spec=spec=t_strdup(p_spec);
|
||||
f=_f;
|
||||
ff=_ff;
|
||||
fp=_fp;
|
||||
}
|
||||
operator T_CHAR*()
|
||||
{
|
||||
run();
|
||||
return str.GetBuf();
|
||||
}
|
||||
~FMT()
|
||||
{
|
||||
if (org_spec) free(org_spec);
|
||||
}
|
||||
};
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
UINT tagz_format(const T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void *fp,T_CHAR* out,UINT max)
|
||||
{
|
||||
T_CHAR * zz=tagz_format_r(spec,f,ff,fp);
|
||||
UINT r=0;
|
||||
while(r<max-1 && zz[r])
|
||||
{
|
||||
out[r]=zz[r];
|
||||
r++;
|
||||
}
|
||||
out[r]=0;
|
||||
free(zz);
|
||||
return r;
|
||||
}
|
||||
|
||||
T_CHAR * tagz_format_r(const T_CHAR* spec,TAGFUNC f,TAGFREEFUNC ff,void * fp)
|
||||
{
|
||||
return FMT(spec,f,ff,fp);
|
||||
}
|
||||
|
||||
//char tagz_manual[]="TODO: WTFM";
|
||||
|
||||
const char tagz_manual[]="Syntax reference: \n"
|
||||
"\n"
|
||||
"* %tagname% - inserts field named <tagname>, eg. \"%artist%\"\n"
|
||||
"* $abbr(x) - inserts abbreviation of x, eg. \"$abbr(%album%)\" - will convert album name of \"Final Fantasy VI\" to \"FFVI\"\n"
|
||||
"* $abbr(x,y) - inserts abbreviation of x if x is longer than y characters; otherwise inserts full value of x, eg. \"$abbr(%album%,10)\"\n"
|
||||
"* $lower(x), $upper(x) - converts x to in lower/uppercase, eg. \"$upper(%title%)\"\n"
|
||||
"* $num(x,y) - displays x number and pads with zeros up to y characters (useful for track numbers), eg. $num(%tracknumber%,2)\n"
|
||||
"* $caps(x) - converts first letter in every word of x to uppercase, and all other letters to lowercase, eg. \"blah BLAH\" -> \"Blah Blah\"\n"
|
||||
"* $caps2(x) - similar to $caps, but leaves uppercase letters as they are, eg. \"blah BLAH\" -> \"Blah BLAH\"\n"
|
||||
"* $if(A,B,C) - if A contains at least one valid tag, displays B, otherwise displays C; eg. \"$if(%artist%,%artist%,unknown artist)\" will display artist name if present; otherwise will display \"unknown artist\"; note that \"$if(A,A,)\" is equivalent to \"[A]\" (see below)\n"
|
||||
"* $if2(A,B) - equals to $if(A,A,B)\n"
|
||||
"* $longest(A,B,C,....) - compares lengths of output strings produced by A,B,C... and displays the longest one, eg. \"$longest(%title%,%comment%)\" will display either title if it's longer than comment; otherwise it will display comment\n"
|
||||
"* $pad(x,y) - pads x with spaces up to y characters\n"
|
||||
"* $cut(x,y) - truncates x to y characters\n"
|
||||
"* $padcut(x,y) - pads x to y characters and truncates to y if longer\n"
|
||||
"* [ .... ] - displays contents of brackets only if at least one of fields referenced inside has been found, eg. \"%artist% - [%album% / ]%title%\" will hide [] block if album field is not present\n"
|
||||
"* \' (single quotation mark) - outputs raw text without parsing, eg, \'blah$blah%blah[][]\' will output the contained string and ignore all reserved characters (%,$,[,]) in it; you can use this feature to insert square brackets for an example.\n"
|
||||
"\n"
|
||||
"eg. \"[%artist% - ][$abbr(%album%,10)[ %tracknumber%] / ]%title%[ %streamtitle%]\"\n";
|
||||
|
||||
|
||||
}
|
||||
28
src/plugin_winamp2/tagz.h
Normal file
28
src/plugin_winamp2/tagz.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef UINT
|
||||
typedef unsigned int UINT;
|
||||
#endif
|
||||
|
||||
#ifdef TAGZ_UNICODE
|
||||
typedef unsigned short T_CHAR;
|
||||
#else
|
||||
#define T_CHAR char
|
||||
#endif
|
||||
|
||||
typedef const T_CHAR* (*TAGFUNC)(const T_CHAR *tag,void *p); // return 0 if not found
|
||||
typedef void (*TAGFREEFUNC)(const T_CHAR *tag,void *p);
|
||||
|
||||
|
||||
UINT tagz_format(const T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void *fp,T_CHAR * out,UINT max);
|
||||
T_CHAR * tagz_format_r(const T_CHAR * spec,TAGFUNC f,TAGFREEFUNC ff,void * fp);
|
||||
|
||||
extern const char tagz_manual[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user