Second patch from X-Fixer: file info dialog, tag editor, tag formatting in plugin config

This commit is contained in:
Josh Coalson
2003-01-14 09:01:31 +00:00
parent ecf57b7514
commit 4563fa26a1
14 changed files with 1845 additions and 680 deletions

View File

@@ -23,5 +23,7 @@ EXTRA_DIST = \
config.c \
in_flac.c \
in_flac.dsp \
infobox.c \
infobox.h \
resource.h \
resource.rc

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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(&param.t);
FLAC_plugin__canonical_tag_get_combined(filename, &param.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, &param, 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(&param.t);
}

View File

@@ -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

View File

@@ -16,6 +16,5 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
EXTRA_DIST = \
frontend.h \
in2.h \
out.h

View File

@@ -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

View File

@@ -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

View 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);
}

View File

@@ -0,0 +1,5 @@
//
// prototypes
//
void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename);

View File

@@ -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

View File

@@ -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
View 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
View 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