diff --git a/src/plugin_winamp2/Makefile.am b/src/plugin_winamp2/Makefile.am index 311d43b3..2cf40f36 100644 --- a/src/plugin_winamp2/Makefile.am +++ b/src/plugin_winamp2/Makefile.am @@ -23,5 +23,7 @@ EXTRA_DIST = \ config.c \ in_flac.c \ in_flac.dsp \ + infobox.c \ + infobox.h \ resource.h \ resource.rc diff --git a/src/plugin_winamp2/config.c b/src/plugin_winamp2/config.c index a794a317..ff894e04 100644 --- a/src/plugin_winamp2/config.c +++ b/src/plugin_winamp2/config.c @@ -1,24 +1,28 @@ #include #include #include -#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; iall[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; iall[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; } diff --git a/src/plugin_winamp2/config.h b/src/plugin_winamp2/config.h index c805efe7..60e5efa3 100644 --- a/src/plugin_winamp2/config.h +++ b/src/plugin_winamp2/config.h @@ -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); diff --git a/src/plugin_winamp2/in_flac.c b/src/plugin_winamp2/in_flac.c index adf6a3d8..8a7fe886 100644 --- a/src/plugin_winamp2/in_flac.c +++ b/src/plugin_winamp2/in_flac.c @@ -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); } diff --git a/src/plugin_winamp2/in_flac.dsp b/src/plugin_winamp2/in_flac.dsp index 0edb4668..2c792e38 100644 --- a/src/plugin_winamp2/in_flac.dsp +++ b/src/plugin_winamp2/in_flac.dsp @@ -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 diff --git a/src/plugin_winamp2/include/winamp2/Makefile.am b/src/plugin_winamp2/include/winamp2/Makefile.am index c3f80d25..e774651c 100644 --- a/src/plugin_winamp2/include/winamp2/Makefile.am +++ b/src/plugin_winamp2/include/winamp2/Makefile.am @@ -16,6 +16,5 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. EXTRA_DIST = \ - frontend.h \ in2.h \ out.h diff --git a/src/plugin_winamp2/include/winamp2/frontend.h b/src/plugin_winamp2/include/winamp2/frontend.h deleted file mode 100644 index d5f7e202..00000000 --- a/src/plugin_winamp2/include/winamp2/frontend.h +++ /dev/null @@ -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 \\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 diff --git a/src/plugin_winamp2/include/winamp2/in2.h b/src/plugin_winamp2/include/winamp2/in2.h index 8886027d..46e22f04 100644 --- a/src/plugin_winamp2/include/winamp2/in2.h +++ b/src/plugin_winamp2/include/winamp2/in2.h @@ -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 diff --git a/src/plugin_winamp2/infobox.c b/src/plugin_winamp2/infobox.c new file mode 100644 index 00000000..43a1fe51 --- /dev/null +++ b/src/plugin_winamp2/infobox.c @@ -0,0 +1,290 @@ +#include +#include +#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; ifilename, 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); +} diff --git a/src/plugin_winamp2/infobox.h b/src/plugin_winamp2/infobox.h new file mode 100644 index 00000000..2d562e0c --- /dev/null +++ b/src/plugin_winamp2/infobox.h @@ -0,0 +1,5 @@ +// +// prototypes +// + +void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename); diff --git a/src/plugin_winamp2/resource.h b/src/plugin_winamp2/resource.h index fc6a6c93..f13f3bdc 100644 --- a/src/plugin_winamp2/resource.h +++ b/src/plugin_winamp2/resource.h @@ -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 diff --git a/src/plugin_winamp2/resource.rc b/src/plugin_winamp2/resource.rc index 48c9cdae..6300877b 100644 --- a/src/plugin_winamp2/resource.rc +++ b/src/plugin_winamp2/resource.rc @@ -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 ///////////////////////////////////////////////////////////////////////////// diff --git a/src/plugin_winamp2/tagz.cpp b/src/plugin_winamp2/tagz.cpp new file mode 100644 index 00000000..ebece859 --- /dev/null +++ b/src/plugin_winamp2/tagz.cpp @@ -0,0 +1,923 @@ +#include +#include +#include +#include +#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'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])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;n3) 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 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;ns2 || (*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; nf; + 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, 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"; + + +} diff --git a/src/plugin_winamp2/tagz.h b/src/plugin_winamp2/tagz.h new file mode 100644 index 00000000..b7a45c40 --- /dev/null +++ b/src/plugin_winamp2/tagz.h @@ -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