--- src/plugin_bmp/plugin.c-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/plugin.c 2005-08-10 12:49:43.000000000 +0200 @@ -0,0 +1,814 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_LANGINFO_CODESET +#include +#endif + +#include "FLAC/all.h" +#include "plugin_common/all.h" +#include "share/grabbag.h" +#include "share/replaygain_synthesis.h" +#include "configure.h" +#include "charset.h" +#include "http.h" +#include "tag.h" + +#ifdef min +#undef min +#endif +#define min(x,y) ((x)<(y)?(x):(y)) + +/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ +#ifdef _MSC_VER +#define FLAC__U64L(x) x +#else +#define FLAC__U64L(x) x##LLU +#endif + +extern void FLAC_XMMS__file_info_box(char *filename); + +typedef struct { + FLAC__bool abort_flag; + FLAC__bool is_playing; + FLAC__bool eof; + FLAC__bool play_thread_open; /* if true, is_playing must also be true */ + unsigned total_samples; + unsigned bits_per_sample; + unsigned channels; + unsigned sample_rate; + unsigned length_in_msec; + gchar *title; + AFormat sample_format; + unsigned sample_format_bytes_per_sample; + int seek_to_in_sec; + FLAC__bool has_replaygain; + double replay_scale; + DitherContext dither_context; +} file_info_struct; + +typedef FLAC__StreamDecoderWriteStatus (*WriteCallback) (const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +typedef void (*MetadataCallback) (const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +typedef void (*ErrorCallback) (const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +typedef struct { + FLAC__bool seekable; + void* (*new_decoder) (void); + FLAC__bool (*set_md5_checking) (void *decoder, FLAC__bool value); + FLAC__bool (*set_source) (void *decoder, const char* source); + FLAC__bool (*set_metadata_ignore_all) (void *decoder); + FLAC__bool (*set_metadata_respond) (void *decoder, FLAC__MetadataType type); + FLAC__bool (*set_write_callback) (void *decoder, WriteCallback value); + FLAC__bool (*set_metadata_callback) (void *decoder, MetadataCallback value); + FLAC__bool (*set_error_callback) (void *decoder, ErrorCallback value); + FLAC__bool (*set_client_data) (void *decoder, void *value); + FLAC__bool (*decoder_init) (void *decoder); + void (*safe_decoder_finish) (void *decoder); + void (*safe_decoder_delete) (void *decoder); + FLAC__bool (*process_until_end_of_metadata) (void *decoder); + FLAC__bool (*process_single) (void *decoder); + FLAC__bool (*is_eof) (void *decoder); +} decoder_funcs_t; + +#define NUM_DECODER_TYPES 2 +typedef enum { + DECODER_FILE, + DECODER_HTTP +} decoder_t; + +static void FLAC_XMMS__init(void); +static int FLAC_XMMS__is_our_file(char *filename); +static void FLAC_XMMS__play_file(char *filename); +static void FLAC_XMMS__stop(void); +static void FLAC_XMMS__pause(short p); +static void FLAC_XMMS__seek(int time); +static int FLAC_XMMS__get_time(void); +static void FLAC_XMMS__cleanup(void); +static void FLAC_XMMS__get_song_info(char *filename, char **title, int *length); + +static void *play_loop_(void *arg); + +static FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fnsp); +static void file_decoder_safe_decoder_finish_(void *decoder); +static void file_decoder_safe_decoder_delete_(void *decoder); +static FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +static void init_decoder_func_tables(void); +static decoder_t source_to_decoder_type (const char *source); + +InputPlugin flac_ip = +{ + NULL, + NULL, + "FLAC Player v" VERSION, + FLAC_XMMS__init, + FLAC_XMMS__aboutbox, + FLAC_XMMS__configure, + FLAC_XMMS__is_our_file, + NULL, + FLAC_XMMS__play_file, + FLAC_XMMS__stop, + FLAC_XMMS__pause, + FLAC_XMMS__seek, + NULL, + FLAC_XMMS__get_time, + NULL, + NULL, + FLAC_XMMS__cleanup, + NULL, + NULL, + NULL, + NULL, + FLAC_XMMS__get_song_info, + FLAC_XMMS__file_info_box, + NULL +}; + +#define SAMPLES_PER_WRITE 512 +#define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * FLAC_PLUGIN__MAX_SUPPORTED_CHANNELS * (24/8)) +static FLAC__byte sample_buffer_[SAMPLE_BUFFER_SIZE]; +static unsigned sample_buffer_first_, sample_buffer_last_; + +static void *decoder_ = 0; +static file_info_struct file_info_; +static pthread_t decode_thread_; +static FLAC__bool audio_error_ = false; +static FLAC__bool is_big_endian_host_; + +#define BITRATE_HIST_SEGMENT_MSEC 500 +/* 500ms * 50 = 25s should be enough */ +#define BITRATE_HIST_SIZE 50 +static unsigned bitrate_history_[BITRATE_HIST_SIZE]; + +/* A table of sets of decoder functions, indexed by decoder_t */ +static const decoder_funcs_t* DECODER_FUNCS[NUM_DECODER_TYPES]; + +static decoder_funcs_t const * decoder_func_table_; + + +InputPlugin *get_iplugin_info(void) +{ + flac_ip.description = g_strdup_printf("Reference FLAC Player v%s", FLAC__VERSION_STRING); + return &flac_ip; +} + +void set_track_info(const char* title, int length_in_msec) +{ + if (file_info_.is_playing) { + flac_ip.set_info((char*) title, length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); + } +} + +static gchar* homedir(void) +{ + gchar *result; + char *env_home = getenv("HOME"); + if (env_home) { + result = g_strdup (env_home); + } else { + uid_t uid = getuid(); + struct passwd *pwent; + do { + pwent = getpwent(); + } while (pwent && pwent->pw_uid != uid); + result = pwent ? g_strdup (pwent->pw_dir) : NULL; + endpwent(); + } + return result; +} + +void FLAC_XMMS__init(void) +{ + ConfigFile *cfg; + FLAC__uint32 test = 1; + + is_big_endian_host_ = (*((FLAC__byte*)(&test)))? false : true; + + flac_cfg.title.tag_override = FALSE; + g_free(flac_cfg.title.tag_format); + flac_cfg.title.convert_char_set = FALSE; + + cfg = xmms_cfg_open_default_file(); + + /* title */ + + xmms_cfg_read_boolean(cfg, "flac", "title.tag_override", &flac_cfg.title.tag_override); + + if(!xmms_cfg_read_string(cfg, "flac", "title.tag_format", &flac_cfg.title.tag_format)) + flac_cfg.title.tag_format = g_strdup("%p - %t"); + + xmms_cfg_read_boolean(cfg, "flac", "title.convert_char_set", &flac_cfg.title.convert_char_set); + + if(!xmms_cfg_read_string(cfg, "flac", "title.user_char_set", &flac_cfg.title.user_char_set)) + flac_cfg.title.user_char_set = FLAC_plugin__charset_get_current(); + + /* replaygain */ + + xmms_cfg_read_boolean(cfg, "flac", "output.replaygain.enable", &flac_cfg.output.replaygain.enable); + + xmms_cfg_read_boolean(cfg, "flac", "output.replaygain.album_mode", &flac_cfg.output.replaygain.album_mode); + + if(!xmms_cfg_read_int(cfg, "flac", "output.replaygain.preamp", &flac_cfg.output.replaygain.preamp)) + flac_cfg.output.replaygain.preamp = 0; + + xmms_cfg_read_boolean(cfg, "flac", "output.replaygain.hard_limit", &flac_cfg.output.replaygain.hard_limit); + + xmms_cfg_read_boolean(cfg, "flac", "output.resolution.normal.dither_24_to_16", &flac_cfg.output.resolution.normal.dither_24_to_16); + xmms_cfg_read_boolean(cfg, "flac", "output.resolution.replaygain.dither", &flac_cfg.output.resolution.replaygain.dither); + + if(!xmms_cfg_read_int(cfg, "flac", "output.resolution.replaygain.noise_shaping", &flac_cfg.output.resolution.replaygain.noise_shaping)) + flac_cfg.output.resolution.replaygain.noise_shaping = 1; + + if(!xmms_cfg_read_int(cfg, "flac", "output.resolution.replaygain.bps_out", &flac_cfg.output.resolution.replaygain.bps_out)) + flac_cfg.output.resolution.replaygain.bps_out = 16; + + /* stream */ + + xmms_cfg_read_int(cfg, "flac", "stream.http_buffer_size", &flac_cfg.stream.http_buffer_size); + xmms_cfg_read_int(cfg, "flac", "stream.http_prebuffer", &flac_cfg.stream.http_prebuffer); + xmms_cfg_read_boolean(cfg, "flac", "stream.use_proxy", &flac_cfg.stream.use_proxy); + xmms_cfg_read_string(cfg, "flac", "stream.proxy_host", &flac_cfg.stream.proxy_host); + xmms_cfg_read_int(cfg, "flac", "stream.proxy_port", &flac_cfg.stream.proxy_port); + xmms_cfg_read_boolean(cfg, "flac", "stream.proxy_use_auth", &flac_cfg.stream.proxy_use_auth); + xmms_cfg_read_string(cfg, "flac", "stream.proxy_user", &flac_cfg.stream.proxy_user); + xmms_cfg_read_string(cfg, "flac", "stream.proxy_pass", &flac_cfg.stream.proxy_pass); + xmms_cfg_read_boolean(cfg, "flac", "stream.save_http_stream", &flac_cfg.stream.save_http_stream); + if (!xmms_cfg_read_string(cfg, "flac", "stream.save_http_path", &flac_cfg.stream.save_http_path) || + ! *flac_cfg.stream.save_http_path) { + if (flac_cfg.stream.save_http_path && *flac_cfg.stream.save_http_path) + g_free (flac_cfg.stream.save_http_path); + flac_cfg.stream.save_http_path = homedir(); + } + xmms_cfg_read_boolean(cfg, "flac", "stream.cast_title_streaming", &flac_cfg.stream.cast_title_streaming); + xmms_cfg_read_boolean(cfg, "flac", "stream.use_udp_channel", &flac_cfg.stream.use_udp_channel); + + init_decoder_func_tables(); + decoder_func_table_ = DECODER_FUNCS [DECODER_FILE]; + decoder_ = decoder_func_table_ -> new_decoder(); + + xmms_cfg_free(cfg); +} + +int FLAC_XMMS__is_our_file(char *filename) +{ + char *ext; + + ext = strrchr(filename, '.'); + if(ext) + if(!strcasecmp(ext, ".flac") || !strcasecmp(ext, ".fla")) + return 1; + return 0; +} + +void FLAC_XMMS__play_file(char *filename) +{ + FILE *f; + + sample_buffer_first_ = sample_buffer_last_ = 0; + audio_error_ = false; + file_info_.abort_flag = false; + file_info_.is_playing = false; + file_info_.eof = false; + file_info_.play_thread_open = false; + file_info_.has_replaygain = false; + + if (source_to_decoder_type (filename) == DECODER_FILE) { + if(0 == (f = fopen(filename, "r"))) + return; + fclose(f); + } + + if(decoder_ == 0) + return; + + if(!safe_decoder_init_(filename, &decoder_, &decoder_func_table_)) + return; + + if(file_info_.has_replaygain && flac_cfg.output.replaygain.enable) { + if(flac_cfg.output.resolution.replaygain.bps_out == 8) { + file_info_.sample_format = FMT_U8; + file_info_.sample_format_bytes_per_sample = 1; + } + else if(flac_cfg.output.resolution.replaygain.bps_out == 16) { + file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; + file_info_.sample_format_bytes_per_sample = 2; + } + else { + /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ + fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", flac_cfg.output.resolution.replaygain.bps_out); + decoder_func_table_ -> safe_decoder_finish(decoder_); + return; + } + } + else { + if(file_info_.bits_per_sample == 8) { + file_info_.sample_format = FMT_U8; + file_info_.sample_format_bytes_per_sample = 1; + } + else if(file_info_.bits_per_sample == 16 || (file_info_.bits_per_sample == 24 && flac_cfg.output.resolution.normal.dither_24_to_16)) { + file_info_.sample_format = (is_big_endian_host_) ? FMT_S16_BE : FMT_S16_LE; + file_info_.sample_format_bytes_per_sample = 2; + } + else { + /*@@@ need some error here like wa2: MessageBox(mod_.hMainWindow, "ERROR: plugin can only handle 8/16-bit samples\n", "ERROR: plugin can only handle 8/16-bit samples", 0); */ + fprintf(stderr, "libxmms-flac: can't handle %d bit output\n", file_info_.bits_per_sample); + decoder_func_table_ -> safe_decoder_finish(decoder_); + return; + } + } + FLAC__replaygain_synthesis__init_dither_context(&file_info_.dither_context, file_info_.sample_format_bytes_per_sample * 8, flac_cfg.output.resolution.replaygain.noise_shaping); + file_info_.is_playing = true; + + if(flac_ip.output->open_audio(file_info_.sample_format, file_info_.sample_rate, file_info_.channels) == 0) { + audio_error_ = true; + decoder_func_table_ -> safe_decoder_finish(decoder_); + return; + } + + file_info_.title = flac_format_song_title(filename); + flac_ip.set_info(file_info_.title, file_info_.length_in_msec, file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample, file_info_.sample_rate, file_info_.channels); + + file_info_.seek_to_in_sec = -1; + file_info_.play_thread_open = true; + pthread_create(&decode_thread_, NULL, play_loop_, NULL); +} + +void FLAC_XMMS__stop(void) +{ + if(file_info_.is_playing) { + file_info_.is_playing = false; + if(file_info_.play_thread_open) { + file_info_.play_thread_open = false; + pthread_join(decode_thread_, NULL); + } + flac_ip.output->close_audio(); + decoder_func_table_ -> safe_decoder_finish (decoder_); + } +} + +void FLAC_XMMS__pause(short p) +{ + flac_ip.output->pause(p); +} + +void FLAC_XMMS__seek(int time) +{ + if (decoder_func_table_->seekable) { + file_info_.seek_to_in_sec = time; + file_info_.eof = false; + + while(file_info_.seek_to_in_sec != -1) + xmms_usleep(10000); + } +} + +int FLAC_XMMS__get_time(void) +{ + if(audio_error_) + return -2; + if(!file_info_.is_playing || (file_info_.eof && !flac_ip.output->buffer_playing())) + return -1; + else + return flac_ip.output->output_time(); +} + +void FLAC_XMMS__cleanup(void) +{ + decoder_func_table_ -> safe_decoder_delete(decoder_); + decoder_ = 0; +} + +void FLAC_XMMS__get_song_info(char *filename, char **title, int *length_in_msec) +{ + FLAC__StreamMetadata streaminfo; + + if(0 == filename) + filename = ""; + + if(!FLAC__metadata_get_streaminfo(filename, &streaminfo)) { + /* @@@ how to report the error? */ + if(title) { + if (source_to_decoder_type (filename) == DECODER_FILE) { + static const char *errtitle = "Invalid FLAC File: "; + *title = g_malloc(strlen(errtitle) + 1 + strlen(filename) + 1 + 1); + sprintf(*title, "%s\"%s\"", errtitle, filename); + } else { + *title = NULL; + } + } + if(length_in_msec) + *length_in_msec = -1; + return; + } + + if(title) { + *title = flac_format_song_title(filename); + } + if(length_in_msec) + *length_in_msec = (unsigned)((double)streaminfo.data.stream_info.total_samples / (double)streaminfo.data.stream_info.sample_rate * 1000.0 + 0.5); +} + +/*********************************************************************** + * local routines + **********************************************************************/ + +void *play_loop_(void *arg) +{ + unsigned written_time_last = 0, bh_index_last_w = 0, bh_index_last_o = BITRATE_HIST_SIZE, blocksize = 1; + FLAC__uint64 decode_position_last = 0, decode_position_frame_last = 0, decode_position_frame = 0; + + (void)arg; + + while(file_info_.is_playing) { + if(!file_info_.eof) { + while(sample_buffer_last_ - sample_buffer_first_ < SAMPLES_PER_WRITE) { + unsigned s; + + s = sample_buffer_last_ - sample_buffer_first_; + if(decoder_func_table_ -> is_eof(decoder_)) { + file_info_.eof = true; + break; + } + else if (!decoder_func_table_ -> process_single(decoder_)) { + /*@@@ this should probably be a dialog */ + fprintf(stderr, "libxmms-flac: READ ERROR processing frame\n"); + file_info_.eof = true; + break; + } + blocksize = sample_buffer_last_ - sample_buffer_first_ - s; + decode_position_frame_last = decode_position_frame; + if(!decoder_func_table_->seekable || !FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) + decode_position_frame = 0; + } + if(sample_buffer_last_ - sample_buffer_first_ > 0) { + const unsigned n = min(sample_buffer_last_ - sample_buffer_first_, SAMPLES_PER_WRITE); + int bytes = n * file_info_.channels * file_info_.sample_format_bytes_per_sample; + FLAC__byte *sample_buffer_start = sample_buffer_ + sample_buffer_first_ * file_info_.channels * file_info_.sample_format_bytes_per_sample; + unsigned written_time, bh_index_w; + FLAC__uint64 decode_position; + + sample_buffer_first_ += n; + flac_ip.add_vis_pcm(flac_ip.output->written_time(), file_info_.sample_format, file_info_.channels, bytes, sample_buffer_start); + while(flac_ip.output->buffer_free() < (int)bytes && file_info_.is_playing && file_info_.seek_to_in_sec == -1) + xmms_usleep(10000); + if(file_info_.is_playing && file_info_.seek_to_in_sec == -1) + flac_ip.output->write_audio(sample_buffer_start, bytes); + + /* compute current bitrate */ + + written_time = flac_ip.output->written_time(); + bh_index_w = written_time / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; + if(bh_index_w != bh_index_last_w) { + bh_index_last_w = bh_index_w; + decode_position = decode_position_frame - (double)(sample_buffer_last_ - sample_buffer_first_) * (double)(decode_position_frame - decode_position_frame_last) / (double)blocksize; + bitrate_history_[(bh_index_w + BITRATE_HIST_SIZE - 1) % BITRATE_HIST_SIZE] = + decode_position > decode_position_last && written_time > written_time_last ? + 8000 * (decode_position - decode_position_last) / (written_time - written_time_last) : + file_info_.sample_rate * file_info_.channels * file_info_.bits_per_sample; + decode_position_last = decode_position; + written_time_last = written_time; + } + } + else { + file_info_.eof = true; + xmms_usleep(10000); + } + } + else + xmms_usleep(10000); + if(decoder_func_table_->seekable && file_info_.seek_to_in_sec != -1) { + const double distance = (double)file_info_.seek_to_in_sec * 1000.0 / (double)file_info_.length_in_msec; + unsigned target_sample = (unsigned)(distance * (double)file_info_.total_samples); + if(FLAC__file_decoder_seek_absolute(decoder_, (FLAC__uint64)target_sample)) { + flac_ip.output->flush(file_info_.seek_to_in_sec * 1000); + bh_index_last_w = bh_index_last_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; + if(!FLAC__file_decoder_get_decode_position(decoder_, &decode_position_frame)) + decode_position_frame = 0; + file_info_.seek_to_in_sec = -1; + file_info_.eof = false; + sample_buffer_first_ = sample_buffer_last_ = 0; + } + } + else { + /* display the right bitrate from history */ + unsigned bh_index_o = flac_ip.output->output_time() / BITRATE_HIST_SEGMENT_MSEC % BITRATE_HIST_SIZE; + if(bh_index_o != bh_index_last_o && bh_index_o != bh_index_last_w && bh_index_o != (bh_index_last_w + 1) % BITRATE_HIST_SIZE) { + bh_index_last_o = bh_index_o; + flac_ip.set_info(file_info_.title, file_info_.length_in_msec, bitrate_history_[bh_index_o], file_info_.sample_rate, file_info_.channels); + } + } + } + + decoder_func_table_ -> safe_decoder_finish(decoder_); + + /* are these two calls necessary? */ + flac_ip.output->buffer_free(); + flac_ip.output->buffer_free(); + + g_free(file_info_.title); + + pthread_exit(NULL); + return 0; /* to silence the compiler warning about not returning a value */ +} + +/*********** File decoder functions */ + +static FLAC__bool file_decoder_init (void *decoder) +{ + return FLAC__file_decoder_init( (FLAC__FileDecoder*) decoder) == FLAC__FILE_DECODER_OK; +} + +static void file_decoder_safe_decoder_finish_(void *decoder) +{ + if(decoder && FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) != FLAC__FILE_DECODER_UNINITIALIZED) + FLAC__file_decoder_finish((FLAC__FileDecoder *) decoder); +} + +static void file_decoder_safe_decoder_delete_(void *decoder) +{ + if(decoder) { + file_decoder_safe_decoder_finish_(decoder); + FLAC__file_decoder_delete( (FLAC__FileDecoder *) decoder); + } +} + +static FLAC__bool file_decoder_is_eof(void *decoder) +{ + return FLAC__file_decoder_get_state((FLAC__FileDecoder *) decoder) == FLAC__FILE_DECODER_END_OF_FILE; +} + +static const decoder_funcs_t FILE_DECODER_FUNCTIONS = { + true, + (void* (*) (void)) FLAC__file_decoder_new, + (FLAC__bool (*) (void *, FLAC__bool)) FLAC__file_decoder_set_md5_checking, + (FLAC__bool (*) (void *, const char*)) FLAC__file_decoder_set_filename, + (FLAC__bool (*) (void *)) FLAC__file_decoder_set_metadata_ignore_all, + (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__file_decoder_set_metadata_respond, + (FLAC__bool (*) (void *, WriteCallback)) FLAC__file_decoder_set_write_callback, + (FLAC__bool (*) (void *, MetadataCallback)) FLAC__file_decoder_set_metadata_callback, + (FLAC__bool (*) (void *, ErrorCallback)) FLAC__file_decoder_set_error_callback, + (FLAC__bool (*) (void *, void *)) FLAC__file_decoder_set_client_data, + (FLAC__bool (*) (void *)) file_decoder_init, + (void (*) (void *)) file_decoder_safe_decoder_finish_, + (void (*) (void *)) file_decoder_safe_decoder_delete_, + (FLAC__bool (*) (void *)) FLAC__file_decoder_process_until_end_of_metadata, + (FLAC__bool (*) (void *)) FLAC__file_decoder_process_single, + file_decoder_is_eof +}; + +/*********** HTTP decoder functions */ + +static gchar *url_; + +static FLAC__bool http_decoder_set_md5_checking (void *decoder, FLAC__bool value) +{ + (void) value; + // operation unsupported + return FLAC__stream_decoder_get_state ((const FLAC__StreamDecoder *) decoder) == + FLAC__STREAM_DECODER_UNINITIALIZED; +} + +static FLAC__bool http_decoder_set_url (void *decoder, const char* url) +{ + (void) decoder; + url_ = g_strdup (url); + return true; +} + +static FLAC__StreamDecoderReadStatus http_decoder_read_callback (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) +{ + (void) decoder; + (void) client_data; + *bytes = flac_http_read (buffer, *bytes); + return *bytes ? FLAC__STREAM_DECODER_READ_STATUS_CONTINUE : FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; +} + +static FLAC__bool http_decoder_init (void *decoder) +{ + flac_http_open (url_, 0); + g_free (url_); + FLAC__stream_decoder_set_read_callback (decoder, http_decoder_read_callback); + return FLAC__stream_decoder_init( (FLAC__StreamDecoder*) decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA; +} + +static void http_decoder_safe_decoder_finish_(void *decoder) +{ + if(decoder && FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) { + FLAC__stream_decoder_finish((FLAC__StreamDecoder *) decoder); + flac_http_close(); + } +} + +static void http_decoder_safe_decoder_delete_(void *decoder) +{ + if(decoder) { + http_decoder_safe_decoder_finish_(decoder); + FLAC__stream_decoder_delete( (FLAC__StreamDecoder *) decoder); + } +} + +static FLAC__bool http_decoder_is_eof(void *decoder) +{ + return FLAC__stream_decoder_get_state((FLAC__StreamDecoder *) decoder) == FLAC__STREAM_DECODER_END_OF_STREAM; +} + +static const decoder_funcs_t HTTP_DECODER_FUNCTIONS = { + false, + (void* (*) (void)) FLAC__stream_decoder_new, + http_decoder_set_md5_checking, + (FLAC__bool (*) (void *, const char*)) http_decoder_set_url, + (FLAC__bool (*) (void *)) FLAC__stream_decoder_set_metadata_ignore_all, + (FLAC__bool (*) (void *, FLAC__MetadataType)) FLAC__stream_decoder_set_metadata_respond, + (FLAC__bool (*) (void *, WriteCallback)) FLAC__stream_decoder_set_write_callback, + (FLAC__bool (*) (void *, MetadataCallback)) FLAC__stream_decoder_set_metadata_callback, + (FLAC__bool (*) (void *, ErrorCallback)) FLAC__stream_decoder_set_error_callback, + (FLAC__bool (*) (void *, void *)) FLAC__stream_decoder_set_client_data, + (FLAC__bool (*) (void *)) http_decoder_init, + (void (*) (void *)) http_decoder_safe_decoder_finish_, + (void (*) (void *)) http_decoder_safe_decoder_delete_, + (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_until_end_of_metadata, + (FLAC__bool (*) (void *)) FLAC__stream_decoder_process_single, + http_decoder_is_eof +}; + +static decoder_funcs_t const *decoder_func_table_; + +static void init_decoder_func_tables(void) +{ + DECODER_FUNCS [DECODER_FILE] = & FILE_DECODER_FUNCTIONS; + DECODER_FUNCS [DECODER_HTTP] = & HTTP_DECODER_FUNCTIONS; +} + +static decoder_t source_to_decoder_type (const char *source) +{ + return strncasecmp(source, "http://", 7) ? DECODER_FILE : DECODER_HTTP; +} + +static void change_decoder_if_needed (decoder_t new_decoder_type, void **decoderp, decoder_funcs_t const ** fntabp) +{ + const decoder_funcs_t *new_fn_table = DECODER_FUNCS [new_decoder_type]; + if (*fntabp != new_fn_table) { + (*fntabp)->safe_decoder_delete(*decoderp); + *fntabp = new_fn_table; + *decoderp = new_fn_table -> new_decoder(); + } +} + +FLAC__bool safe_decoder_init_(const char *filename, void **decoderp, decoder_funcs_t const ** fntabp) +{ + if(decoderp == 0 || *decoderp == 0) + return false; + + (*fntabp)->safe_decoder_finish(*decoderp); + + change_decoder_if_needed(source_to_decoder_type(filename), decoderp, fntabp); + + { + decoder_funcs_t const *fntab = *fntabp; + void *decoder = *decoderp; + + decoder = *decoderp; + fntab = *fntabp; + + fntab -> set_md5_checking(decoder, false); + fntab -> set_source(decoder, filename); + fntab -> set_metadata_ignore_all(decoder); + fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); + fntab -> set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + fntab -> set_write_callback(decoder, write_callback_); + fntab -> set_metadata_callback(decoder, metadata_callback_); + fntab -> set_error_callback(decoder, error_callback_); + fntab -> set_client_data(decoder, &file_info_); + if(!fntab -> decoder_init(decoder)) + return false; + + if(!fntab -> process_until_end_of_metadata(decoder)) + return false; + } + + return true; +} + +FLAC__StreamDecoderWriteStatus write_callback_(const void *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + const unsigned channels = file_info->channels, wide_samples = frame->header.blocksize; + const unsigned bits_per_sample = file_info->bits_per_sample; + FLAC__byte *sample_buffer_start; + + (void)decoder; + + if(file_info->abort_flag) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + + if((sample_buffer_last_ + wide_samples) > (SAMPLE_BUFFER_SIZE / (channels * file_info->sample_format_bytes_per_sample))) { + memmove(sample_buffer_, sample_buffer_ + sample_buffer_first_ * channels * file_info->sample_format_bytes_per_sample, (sample_buffer_last_ - sample_buffer_first_) * channels * file_info->sample_format_bytes_per_sample); + sample_buffer_last_ -= sample_buffer_first_; + sample_buffer_first_ = 0; + } + sample_buffer_start = sample_buffer_ + sample_buffer_last_ * channels * file_info->sample_format_bytes_per_sample; + if(file_info->has_replaygain && flac_cfg.output.replaygain.enable) { + FLAC__replaygain_synthesis__apply_gain( + sample_buffer_start, + !is_big_endian_host_, + file_info->sample_format_bytes_per_sample == 1, /* unsigned_data_out */ + buffer, + wide_samples, + channels, + bits_per_sample, + file_info->sample_format_bytes_per_sample * 8, + file_info->replay_scale, + flac_cfg.output.replaygain.hard_limit, + flac_cfg.output.resolution.replaygain.dither, + &file_info->dither_context + ); + } + else if(is_big_endian_host_) { + FLAC__plugin_common__pack_pcm_signed_big_endian( + sample_buffer_start, + buffer, + wide_samples, + channels, + bits_per_sample, + file_info->sample_format_bytes_per_sample * 8 + ); + } + else { + FLAC__plugin_common__pack_pcm_signed_little_endian( + sample_buffer_start, + buffer, + wide_samples, + channels, + bits_per_sample, + file_info->sample_format_bytes_per_sample * 8 + ); + } + + sample_buffer_last_ += wide_samples; + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +void metadata_callback_(const void *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + file_info_struct *file_info = (file_info_struct *)client_data; + (void)decoder; + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + FLAC__ASSERT(metadata->data.stream_info.total_samples < FLAC__U64L(0x100000000)); /* this plugin can only handle < 4 gigasamples */ + file_info->total_samples = (unsigned)(metadata->data.stream_info.total_samples&0xffffffff); + 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; + file_info->length_in_msec = (unsigned)((double)file_info->total_samples / (double)file_info->sample_rate * 1000.0 + 0.5); + } + 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)) { + 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); + } + } +} + +void error_callback_(const void *decoder, FLAC__StreamDecoderErrorStatus status, void *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; +} --- src/plugin_bmp/plugin.h-dist 2005-05-25 16:04:46.000000000 +0200 +++ src/plugin_bmp/plugin.h 2005-05-25 15:40:35.000000000 +0200 @@ -0,0 +1,24 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2004,2005 Josh Coalson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef FLAC__PLUGIN_XMMS__PLUGIN_H +#define FLAC__PLUGIN_XMMS__PLUGIN_H + +void set_track_info(const char* title, int length_in_msec); + +#endif --- src/plugin_bmp/configure.c-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/configure.c 2005-05-25 15:44:34.000000000 +0200 @@ -0,0 +1,818 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Based on mpg123 plugin + * and prefs.c - 2000/05/06 + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 2000-2002 Jerome Couderc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "plugin_common/locale_hack.h" +#include "share/replaygain_synthesis.h" /* for NOISE_SHAPING_LOW */ +#include "charset.h" +#include "configure.h" + +/* + * Initialize Global Valueable + */ +flac_config_t flac_cfg = { + /* title */ + { + FALSE, /* tag_override */ + NULL, /* tag_format */ + FALSE, /* convert_char_set */ + NULL /* user_char_set */ + }, + /* stream */ + { + 100 /* KB */, /* http_buffer_size */ + 50, /* http_prebuffer */ + FALSE, /* use_proxy */ + "", /* proxy_host */ + 0, /* proxy_port */ + FALSE, /* proxy_use_auth */ + "", /* proxy_user */ + "", /* proxy_pass */ + FALSE, /* save_http_stream */ + "", /* save_http_path */ + FALSE, /* cast_title_streaming */ + FALSE /* use_udp_channel */ + }, + /* output */ + { + /* replaygain */ + { + FALSE, /* enable */ + TRUE, /* album_mode */ + 0, /* preamp */ + FALSE /* hard_limit */ + }, + /* resolution */ + { + /* normal */ + { + TRUE /* dither_24_to_16 */ + }, + /* replaygain */ + { + TRUE, /* dither */ + NOISE_SHAPING_LOW, /* noise_shaping */ + 16 /* bps_out */ + } + } + } +}; + + +static GtkWidget *flac_configurewin = NULL; +static GtkWidget *vbox, *notebook; + +static GtkWidget *title_tag_override, *title_tag_box, *title_tag_entry, *title_desc; +static GtkWidget *convert_char_set, *fileCharacterSetEntry, *userCharacterSetEntry; +static GtkWidget *replaygain_enable, *replaygain_album_mode; +static GtkWidget *replaygain_preamp_hscale, *replaygain_preamp_label, *replaygain_hard_limit; +static GtkObject *replaygain_preamp; +static GtkWidget *resolution_normal_dither_24_to_16; +static GtkWidget *resolution_replaygain_dither; +static GtkWidget *resolution_replaygain_noise_shaping_frame; +static GtkWidget *resolution_replaygain_noise_shaping_radio_none; +static GtkWidget *resolution_replaygain_noise_shaping_radio_low; +static GtkWidget *resolution_replaygain_noise_shaping_radio_medium; +static GtkWidget *resolution_replaygain_noise_shaping_radio_high; +static GtkWidget *resolution_replaygain_bps_out_frame; +static GtkWidget *resolution_replaygain_bps_out_radio_16bps; +static GtkWidget *resolution_replaygain_bps_out_radio_24bps; + +static GtkObject *streaming_size_adj, *streaming_pre_adj; +static GtkWidget *streaming_proxy_use, *streaming_proxy_host_entry; +static GtkWidget *streaming_proxy_port_entry, *streaming_save_use, *streaming_save_entry; +static GtkWidget *streaming_proxy_auth_use; +static GtkWidget *streaming_proxy_auth_pass_entry, *streaming_proxy_auth_user_entry; +static GtkWidget *streaming_proxy_auth_user_label, *streaming_proxy_auth_pass_label; +#ifdef FLAC_ICECAST +static GtkWidget *streaming_cast_title, *streaming_udp_title; +#endif +static GtkWidget *streaming_proxy_hbox, *streaming_proxy_auth_hbox, *streaming_save_dirbrowser; +static GtkWidget *streaming_save_hbox; + +static gchar *gtk_entry_get_text_1 (GtkWidget *widget); +static void flac_configurewin_ok(GtkWidget * widget, gpointer data); +static void configure_destroy(GtkWidget * w, gpointer data); + +static void flac_configurewin_ok(GtkWidget * widget, gpointer data) +{ + ConfigFile *cfg; + gchar *filename; + + (void)widget, (void)data; /* unused arguments */ + g_free(flac_cfg.title.tag_format); + flac_cfg.title.tag_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(title_tag_entry))); + flac_cfg.title.user_char_set = Charset_Get_Name_From_Title(gtk_entry_get_text_1(userCharacterSetEntry)); + + filename = g_strconcat(g_get_home_dir(), "/.bmp/config", NULL); + cfg = xmms_cfg_open_file(filename); + if (!cfg) + cfg = xmms_cfg_new(); + /* title */ + xmms_cfg_write_boolean(cfg, "flac", "title.tag_override", flac_cfg.title.tag_override); + xmms_cfg_write_string(cfg, "flac", "title.tag_format", flac_cfg.title.tag_format); + xmms_cfg_write_boolean(cfg, "flac", "title.convert_char_set", flac_cfg.title.convert_char_set); + xmms_cfg_write_string(cfg, "flac", "title.user_char_set", flac_cfg.title.user_char_set); + /* output */ + xmms_cfg_write_boolean(cfg, "flac", "output.replaygain.enable", flac_cfg.output.replaygain.enable); + xmms_cfg_write_boolean(cfg, "flac", "output.replaygain.album_mode", flac_cfg.output.replaygain.album_mode); + xmms_cfg_write_int(cfg, "flac", "output.replaygain.preamp", flac_cfg.output.replaygain.preamp); + xmms_cfg_write_boolean(cfg, "flac", "output.replaygain.hard_limit", flac_cfg.output.replaygain.hard_limit); + xmms_cfg_write_boolean(cfg, "flac", "output.resolution.normal.dither_24_to_16", flac_cfg.output.resolution.normal.dither_24_to_16); + xmms_cfg_write_boolean(cfg, "flac", "output.resolution.replaygain.dither", flac_cfg.output.resolution.replaygain.dither); + xmms_cfg_write_int(cfg, "flac", "output.resolution.replaygain.noise_shaping", flac_cfg.output.resolution.replaygain.noise_shaping); + xmms_cfg_write_int(cfg, "flac", "output.resolution.replaygain.bps_out", flac_cfg.output.resolution.replaygain.bps_out); + /* streaming */ + flac_cfg.stream.http_buffer_size = (gint) GTK_ADJUSTMENT(streaming_size_adj)->value; + flac_cfg.stream.http_prebuffer = (gint) GTK_ADJUSTMENT(streaming_pre_adj)->value; + + flac_cfg.stream.use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use)); + g_free(flac_cfg.stream.proxy_host); + flac_cfg.stream.proxy_host = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_host_entry))); + flac_cfg.stream.proxy_port = atoi(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_port_entry))); + + flac_cfg.stream.proxy_use_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use)); + + if(flac_cfg.stream.proxy_user) + g_free(flac_cfg.stream.proxy_user); + flac_cfg.stream.proxy_user = NULL; + if(strlen(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_user_entry))) > 0) + flac_cfg.stream.proxy_user = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_user_entry))); + + if(flac_cfg.stream.proxy_pass) + g_free(flac_cfg.stream.proxy_pass); + flac_cfg.stream.proxy_pass = NULL; + if(strlen(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_pass_entry))) > 0) + flac_cfg.stream.proxy_pass = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_proxy_auth_pass_entry))); + + + flac_cfg.stream.save_http_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use)); + if (flac_cfg.stream.save_http_path && *flac_cfg.stream.save_http_path) + g_free(flac_cfg.stream.save_http_path); + flac_cfg.stream.save_http_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_save_entry))); + +#ifdef FLAC_ICECAST + flac_cfg.stream.cast_title_streaming = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_cast_title)); + flac_cfg.stream.use_udp_channel = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_udp_title)); +#endif + + xmms_cfg_write_int(cfg, "flac", "stream.http_buffer_size", flac_cfg.stream.http_buffer_size); + xmms_cfg_write_int(cfg, "flac", "stream.http_prebuffer", flac_cfg.stream.http_prebuffer); + xmms_cfg_write_boolean(cfg, "flac", "stream.use_proxy", flac_cfg.stream.use_proxy); + xmms_cfg_write_string(cfg, "flac", "stream.proxy_host", flac_cfg.stream.proxy_host); + xmms_cfg_write_int(cfg, "flac", "stream.proxy_port", flac_cfg.stream.proxy_port); + xmms_cfg_write_boolean(cfg, "flac", "stream.proxy_use_auth", flac_cfg.stream.proxy_use_auth); + if(flac_cfg.stream.proxy_user) + xmms_cfg_write_string(cfg, "flac", "stream.proxy_user", flac_cfg.stream.proxy_user); + else + xmms_cfg_remove_key(cfg, "flac", "stream.proxy_user"); + if(flac_cfg.stream.proxy_pass) + xmms_cfg_write_string(cfg, "flac", "stream.proxy_pass", flac_cfg.stream.proxy_pass); + else + xmms_cfg_remove_key(cfg, "flac", "stream.proxy_pass"); + xmms_cfg_write_boolean(cfg, "flac", "stream.save_http_stream", flac_cfg.stream.save_http_stream); + xmms_cfg_write_string(cfg, "flac", "stream.save_http_path", flac_cfg.stream.save_http_path); +#ifdef FLAC_ICECAST + xmms_cfg_write_boolean(cfg, "flac", "stream.cast_title_streaming", flac_cfg.stream.cast_title_streaming); + xmms_cfg_write_boolean(cfg, "flac", "stream.use_udp_channel", flac_cfg.stream.use_udp_channel); +#endif + + xmms_cfg_write_file(cfg, filename); + xmms_cfg_free(cfg); + g_free(filename); + gtk_widget_destroy(flac_configurewin); +} + +static void configure_destroy(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ +} + +static void title_tag_override_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.title.tag_override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_tag_override)); + + gtk_widget_set_sensitive(title_tag_box, flac_cfg.title.tag_override); + gtk_widget_set_sensitive(title_desc, flac_cfg.title.tag_override); + +} + +static void convert_char_set_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.title.convert_char_set = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(convert_char_set)); + + gtk_widget_set_sensitive(fileCharacterSetEntry, FALSE); + gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set); +} + +static void replaygain_enable_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.enable = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_enable)); + + gtk_widget_set_sensitive(replaygain_album_mode, flac_cfg.output.replaygain.enable); + gtk_widget_set_sensitive(replaygain_preamp_hscale, flac_cfg.output.replaygain.enable); + gtk_widget_set_sensitive(replaygain_hard_limit, flac_cfg.output.replaygain.enable); +} + +static void replaygain_album_mode_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.album_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_album_mode)); +} + +static void replaygain_hard_limit_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.hard_limit = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(replaygain_hard_limit)); +} + +static void replaygain_preamp_cb(GtkWidget *widget, gpointer data) +{ + GString *gstring = g_string_new(""); + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.replaygain.preamp = (int) floor(GTK_ADJUSTMENT(replaygain_preamp)->value + 0.5); + + g_string_sprintf(gstring, "%i dB", flac_cfg.output.replaygain.preamp); + gtk_label_set_text(GTK_LABEL(replaygain_preamp_label), _(gstring->str)); +} + +static void resolution_normal_dither_24_to_16_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.normal.dither_24_to_16 = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_normal_dither_24_to_16)); +} + +static void resolution_replaygain_dither_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.replaygain.dither = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_dither)); + + gtk_widget_set_sensitive(resolution_replaygain_noise_shaping_frame, flac_cfg.output.resolution.replaygain.dither); +} + +static void resolution_replaygain_noise_shaping_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.replaygain.noise_shaping = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_none))? 0 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_low))? 1 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_medium))? 2 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_high))? 3 : + 0 + ; +} + +static void resolution_replaygain_bps_out_cb(GtkWidget *widget, gpointer data) +{ + (void)widget, (void)data; /* unused arguments */ + flac_cfg.output.resolution.replaygain.bps_out = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_16bps))? 16 : + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_24bps))? 24 : + 16 + ; +} + +static void proxy_use_cb(GtkWidget * w, gpointer data) +{ + gboolean use_proxy, use_proxy_auth; + (void) w; + (void) data; + + use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use)); + use_proxy_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use)); + + gtk_widget_set_sensitive(streaming_proxy_hbox, use_proxy); + gtk_widget_set_sensitive(streaming_proxy_auth_use, use_proxy); + gtk_widget_set_sensitive(streaming_proxy_auth_hbox, use_proxy && use_proxy_auth); +} + +static void proxy_auth_use_cb(GtkWidget *w, gpointer data) +{ + gboolean use_proxy, use_proxy_auth; + (void) w; + (void) data; + + use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use)); + use_proxy_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use)); + + gtk_widget_set_sensitive(streaming_proxy_auth_hbox, use_proxy && use_proxy_auth); +} + +static void streaming_save_dirbrowser_cb(gchar * dir) +{ + gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), dir); +} + +static void streaming_save_browse_cb(GtkWidget * w, gpointer data) +{ + (void) w; + (void) data; + if (!streaming_save_dirbrowser) + { + streaming_save_dirbrowser = xmms_create_dir_browser(_("Select the directory where you want to store the MPEG streams:"), + flac_cfg.stream.save_http_path, GTK_SELECTION_SINGLE, streaming_save_dirbrowser_cb); + gtk_signal_connect(GTK_OBJECT(streaming_save_dirbrowser), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &streaming_save_dirbrowser); + gtk_window_set_transient_for(GTK_WINDOW(streaming_save_dirbrowser), GTK_WINDOW(flac_configurewin)); + gtk_widget_show(streaming_save_dirbrowser); + } +} + +static void streaming_save_use_cb(GtkWidget * w, gpointer data) +{ + gboolean save_stream; + (void) w; + (void) data; + + save_stream = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_save_use)); + + gtk_widget_set_sensitive(streaming_save_hbox, save_stream); +} + + +void FLAC_XMMS__configure(void) +{ + GtkWidget *title_frame, *title_tag_vbox, *title_tag_label; + GtkWidget *replaygain_frame, *resolution_frame, *output_vbox, *resolution_normal_frame, *resolution_replaygain_frame; + GtkWidget *replaygain_vbox, *resolution_hbox, *resolution_normal_vbox, *resolution_replaygain_vbox; + GtkWidget *resolution_replaygain_noise_shaping_vbox; + GtkWidget *resolution_replaygain_bps_out_vbox; + GtkWidget *label, *hbox; + GtkWidget *bbox, *ok, *cancel; + GList *list; + + GtkWidget *streaming_vbox; + GtkWidget *streaming_buf_frame, *streaming_buf_hbox; + GtkWidget *streaming_size_box, *streaming_size_label, *streaming_size_spin; + GtkWidget *streaming_pre_box, *streaming_pre_label, *streaming_pre_spin; + GtkWidget *streaming_proxy_frame, *streaming_proxy_vbox; + GtkWidget *streaming_proxy_port_label, *streaming_proxy_host_label; + GtkWidget *streaming_save_frame, *streaming_save_vbox; + GtkWidget *streaming_save_label, *streaming_save_browse; +#ifdef FLAC_ICECAST + GtkWidget *streaming_cast_frame, *streaming_cast_vbox; +#endif + char *temp; + + if (flac_configurewin != NULL) { + gdk_window_raise(flac_configurewin->window); + return; + } + flac_configurewin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_signal_connect(GTK_OBJECT(flac_configurewin), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &flac_configurewin); + gtk_signal_connect(GTK_OBJECT(flac_configurewin), "destroy", GTK_SIGNAL_FUNC(configure_destroy), &flac_configurewin); + gtk_window_set_title(GTK_WINDOW(flac_configurewin), _("Flac Configuration")); + gtk_window_set_policy(GTK_WINDOW(flac_configurewin), FALSE, FALSE, FALSE); + gtk_container_border_width(GTK_CONTAINER(flac_configurewin), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(flac_configurewin), vbox); + + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); + + /* Title config.. */ + + title_frame = gtk_frame_new(_("Tag Handling")); + gtk_container_border_width(GTK_CONTAINER(title_frame), 5); + + title_tag_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(title_tag_vbox), 5); + gtk_container_add(GTK_CONTAINER(title_frame), title_tag_vbox); + + /* Convert Char Set */ + + convert_char_set = gtk_check_button_new_with_label(_("Convert Character Set")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(convert_char_set), flac_cfg.title.convert_char_set); + gtk_signal_connect(GTK_OBJECT(convert_char_set), "clicked", convert_char_set_cb, NULL); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), convert_char_set, FALSE, FALSE, 0); + /* Combo boxes... */ + hbox = gtk_hbox_new(FALSE,4); + gtk_container_add(GTK_CONTAINER(title_tag_vbox),hbox); + label = gtk_label_new(_("Convert character set from :")); + gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); + fileCharacterSetEntry = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(hbox),fileCharacterSetEntry,TRUE,TRUE,0); + + label = gtk_label_new (_("to :")); + gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); + userCharacterSetEntry = gtk_combo_new(); + gtk_box_pack_start(GTK_BOX(hbox),userCharacterSetEntry,TRUE,TRUE,0); + + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(fileCharacterSetEntry)->entry),FALSE); + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(userCharacterSetEntry)->entry),FALSE); + gtk_combo_set_value_in_list(GTK_COMBO(fileCharacterSetEntry),TRUE,FALSE); + gtk_combo_set_value_in_list(GTK_COMBO(userCharacterSetEntry),TRUE,FALSE); + + list = Charset_Create_List(); + gtk_combo_set_popdown_strings(GTK_COMBO(fileCharacterSetEntry),Charset_Create_List_UTF8_Only()); + gtk_combo_set_popdown_strings(GTK_COMBO(userCharacterSetEntry),list); + gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(userCharacterSetEntry)->entry),Charset_Get_Title_From_Name(flac_cfg.title.user_char_set)); + gtk_widget_set_sensitive(fileCharacterSetEntry, FALSE); + gtk_widget_set_sensitive(userCharacterSetEntry, flac_cfg.title.convert_char_set); + + /* Override Tagging Format */ + + title_tag_override = gtk_check_button_new_with_label(_("Override generic titles")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_tag_override), flac_cfg.title.tag_override); + gtk_signal_connect(GTK_OBJECT(title_tag_override), "clicked", title_tag_override_cb, NULL); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_tag_override, FALSE, FALSE, 0); + + title_tag_box = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(title_tag_box, flac_cfg.title.tag_override); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_tag_box, FALSE, FALSE, 0); + + title_tag_label = gtk_label_new(_("Title format:")); + gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_label, FALSE, FALSE, 0); + + title_tag_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(title_tag_entry), flac_cfg.title.tag_format); + gtk_box_pack_start(GTK_BOX(title_tag_box), title_tag_entry, TRUE, TRUE, 0); + + title_desc = xmms_titlestring_descriptions("pafFetnygc", 2); + gtk_widget_set_sensitive(title_desc, flac_cfg.title.tag_override); + gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_desc, FALSE, FALSE, 0); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), title_frame, gtk_label_new(_("Title"))); + + /* Output config.. */ + + output_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(output_vbox), 5); + + /* replaygain */ + + replaygain_frame = gtk_frame_new(_("ReplayGain")); + gtk_container_border_width(GTK_CONTAINER(replaygain_frame), 5); + gtk_box_pack_start(GTK_BOX(output_vbox), replaygain_frame, TRUE, TRUE, 0); + + replaygain_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(replaygain_vbox), 5); + gtk_container_add(GTK_CONTAINER(replaygain_frame), replaygain_vbox); + + replaygain_enable = gtk_check_button_new_with_label(_("Enable ReplayGain processing")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_enable), flac_cfg.output.replaygain.enable); + gtk_signal_connect(GTK_OBJECT(replaygain_enable), "clicked", replaygain_enable_cb, NULL); + gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_enable, FALSE, FALSE, 0); + + replaygain_album_mode = gtk_check_button_new_with_label(_("Album mode")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_album_mode), flac_cfg.output.replaygain.album_mode); + gtk_signal_connect(GTK_OBJECT(replaygain_album_mode), "clicked", replaygain_album_mode_cb, NULL); + gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_album_mode, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE,3); + gtk_container_add(GTK_CONTAINER(replaygain_vbox),hbox); + label = gtk_label_new(_("Preamp:")); + gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); + replaygain_preamp = gtk_adjustment_new(flac_cfg.output.replaygain.preamp, -24.0, +24.0, 1.0, 6.0, 0.0); + gtk_signal_connect(GTK_OBJECT(replaygain_preamp), "value-changed", replaygain_preamp_cb, NULL); + replaygain_preamp_hscale = gtk_hscale_new(GTK_ADJUSTMENT(replaygain_preamp)); + gtk_scale_set_draw_value(GTK_SCALE(replaygain_preamp_hscale), FALSE); + gtk_box_pack_start(GTK_BOX(hbox),replaygain_preamp_hscale,TRUE,TRUE,0); + replaygain_preamp_label = gtk_label_new(_("0 dB")); + gtk_box_pack_start(GTK_BOX(hbox),replaygain_preamp_label,FALSE,FALSE,0); + gtk_adjustment_value_changed(GTK_ADJUSTMENT(replaygain_preamp)); + + replaygain_hard_limit = gtk_check_button_new_with_label(_("6dB hard limiting")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(replaygain_hard_limit), flac_cfg.output.replaygain.hard_limit); + gtk_signal_connect(GTK_OBJECT(replaygain_hard_limit), "clicked", replaygain_hard_limit_cb, NULL); + gtk_box_pack_start(GTK_BOX(replaygain_vbox), replaygain_hard_limit, FALSE, FALSE, 0); + + replaygain_enable_cb(replaygain_enable, NULL); + + /* resolution */ + + resolution_frame = gtk_frame_new(_("Resolution")); + gtk_container_border_width(GTK_CONTAINER(resolution_frame), 5); + gtk_box_pack_start(GTK_BOX(output_vbox), resolution_frame, TRUE, TRUE, 0); + + resolution_hbox = gtk_hbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(resolution_hbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_frame), resolution_hbox); + + resolution_normal_frame = gtk_frame_new(_("Without ReplayGain")); + gtk_container_border_width(GTK_CONTAINER(resolution_normal_frame), 5); + gtk_box_pack_start(GTK_BOX(resolution_hbox), resolution_normal_frame, TRUE, TRUE, 0); + + resolution_normal_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(resolution_normal_vbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_normal_frame), resolution_normal_vbox); + + resolution_normal_dither_24_to_16 = gtk_check_button_new_with_label(_("Dither 24bps to 16bps")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_normal_dither_24_to_16), flac_cfg.output.resolution.normal.dither_24_to_16); + gtk_signal_connect(GTK_OBJECT(resolution_normal_dither_24_to_16), "clicked", resolution_normal_dither_24_to_16_cb, NULL); + gtk_box_pack_start(GTK_BOX(resolution_normal_vbox), resolution_normal_dither_24_to_16, FALSE, FALSE, 0); + + resolution_replaygain_frame = gtk_frame_new(_("With ReplayGain")); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_frame), 5); + gtk_box_pack_start(GTK_BOX(resolution_hbox), resolution_replaygain_frame, TRUE, TRUE, 0); + + resolution_replaygain_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_vbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_frame), resolution_replaygain_vbox); + + resolution_replaygain_dither = gtk_check_button_new_with_label(_("Enable dithering")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_dither), flac_cfg.output.resolution.replaygain.dither); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_dither), "clicked", resolution_replaygain_dither_cb, NULL); + gtk_box_pack_start(GTK_BOX(resolution_replaygain_vbox), resolution_replaygain_dither, FALSE, FALSE, 0); + + hbox = gtk_hbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(hbox), 5); + gtk_box_pack_start(GTK_BOX(resolution_replaygain_vbox), hbox, TRUE, TRUE, 0); + + resolution_replaygain_noise_shaping_frame = gtk_frame_new(_("Noise shaping")); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_noise_shaping_frame), 5); + gtk_box_pack_start(GTK_BOX(hbox), resolution_replaygain_noise_shaping_frame, TRUE, TRUE, 0); + + resolution_replaygain_noise_shaping_vbox = gtk_vbutton_box_new(); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), 5); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_frame), resolution_replaygain_noise_shaping_vbox); + + resolution_replaygain_noise_shaping_radio_none = gtk_radio_button_new_with_label(NULL, _("none")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 0) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_none), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_none), "clicked", resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_none); + + resolution_replaygain_noise_shaping_radio_low = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("low")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 1) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_low), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_low), "clicked", resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_low); + + resolution_replaygain_noise_shaping_radio_medium = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("medium")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 2) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_medium), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_medium), "clicked", resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_medium); + + resolution_replaygain_noise_shaping_radio_high = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_noise_shaping_radio_none), _("high")); + if(flac_cfg.output.resolution.replaygain.noise_shaping == 3) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_noise_shaping_radio_high), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_noise_shaping_radio_high), "clicked", resolution_replaygain_noise_shaping_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_noise_shaping_vbox), resolution_replaygain_noise_shaping_radio_high); + + resolution_replaygain_bps_out_frame = gtk_frame_new(_("Dither to")); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_bps_out_frame), 5); + gtk_box_pack_start(GTK_BOX(hbox), resolution_replaygain_bps_out_frame, FALSE, FALSE, 0); + + resolution_replaygain_bps_out_vbox = gtk_vbutton_box_new(); + gtk_container_border_width(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), 0); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_frame), resolution_replaygain_bps_out_vbox); + + resolution_replaygain_bps_out_radio_16bps = gtk_radio_button_new_with_label(NULL, _("16 bps")); + if(flac_cfg.output.resolution.replaygain.bps_out == 16) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_16bps), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_bps_out_radio_16bps), "clicked", resolution_replaygain_bps_out_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), resolution_replaygain_bps_out_radio_16bps); + + resolution_replaygain_bps_out_radio_24bps = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(resolution_replaygain_bps_out_radio_16bps), _("24 bps")); + if(flac_cfg.output.resolution.replaygain.bps_out == 24) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolution_replaygain_bps_out_radio_24bps), TRUE); + gtk_signal_connect(GTK_OBJECT(resolution_replaygain_bps_out_radio_24bps), "clicked", resolution_replaygain_bps_out_cb, NULL); + gtk_container_add(GTK_CONTAINER(resolution_replaygain_bps_out_vbox), resolution_replaygain_bps_out_radio_24bps); + + resolution_replaygain_dither_cb(resolution_replaygain_dither, NULL); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), output_vbox, gtk_label_new(_("Output"))); + + /* Streaming */ + + streaming_vbox = gtk_vbox_new(FALSE, 0); + + streaming_buf_frame = gtk_frame_new(_("Buffering:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_buf_frame, FALSE, FALSE, 0); + + streaming_buf_hbox = gtk_hbox_new(TRUE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_buf_hbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_buf_frame), streaming_buf_hbox); + + streaming_size_box = gtk_hbox_new(FALSE, 5); + /*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_size_box,0,1,0,1); */ + gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_size_box, TRUE, TRUE, 0); + streaming_size_label = gtk_label_new(_("Buffer size (kb):")); + gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_label, FALSE, FALSE, 0); + streaming_size_adj = gtk_adjustment_new(flac_cfg.stream.http_buffer_size, 4, 4096, 4, 4, 4); + streaming_size_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_size_adj), 8, 0); + gtk_widget_set_usize(streaming_size_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(streaming_size_box), streaming_size_spin, FALSE, FALSE, 0); + + streaming_pre_box = gtk_hbox_new(FALSE, 5); + /*gtk_table_attach_defaults(GTK_TABLE(streaming_buf_table),streaming_pre_box,1,2,0,1); */ + gtk_box_pack_start(GTK_BOX(streaming_buf_hbox), streaming_pre_box, TRUE, TRUE, 0); + streaming_pre_label = gtk_label_new(_("Pre-buffer (percent):")); + gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_label, FALSE, FALSE, 0); + streaming_pre_adj = gtk_adjustment_new(flac_cfg.stream.http_prebuffer, 0, 90, 1, 1, 1); + streaming_pre_spin = gtk_spin_button_new(GTK_ADJUSTMENT(streaming_pre_adj), 1, 0); + gtk_widget_set_usize(streaming_pre_spin, 60, -1); + gtk_box_pack_start(GTK_BOX(streaming_pre_box), streaming_pre_spin, FALSE, FALSE, 0); + + /* + * Proxy config. + */ + streaming_proxy_frame = gtk_frame_new(_("Proxy:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_proxy_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_proxy_frame, FALSE, FALSE, 0); + + streaming_proxy_vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_proxy_vbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_proxy_frame), streaming_proxy_vbox); + + streaming_proxy_use = gtk_check_button_new_with_label(_("Use proxy")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_proxy_use), flac_cfg.stream.use_proxy); + gtk_signal_connect(GTK_OBJECT(streaming_proxy_use), "clicked", GTK_SIGNAL_FUNC(proxy_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_use, FALSE, FALSE, 0); + + streaming_proxy_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_proxy_hbox, flac_cfg.stream.use_proxy); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_hbox, FALSE, FALSE, 0); + + streaming_proxy_host_label = gtk_label_new(_("Host:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_host_label, FALSE, FALSE, 0); + + streaming_proxy_host_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_host_entry), flac_cfg.stream.proxy_host); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_host_entry, TRUE, TRUE, 0); + + streaming_proxy_port_label = gtk_label_new(_("Port:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_port_label, FALSE, FALSE, 0); + + streaming_proxy_port_entry = gtk_entry_new(); + gtk_widget_set_usize(streaming_proxy_port_entry, 50, -1); + temp = g_strdup_printf("%d", flac_cfg.stream.proxy_port); + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_port_entry), temp); + g_free(temp); + gtk_box_pack_start(GTK_BOX(streaming_proxy_hbox), streaming_proxy_port_entry, FALSE, FALSE, 0); + + streaming_proxy_auth_use = gtk_check_button_new_with_label(_("Use authentication")); + gtk_widget_set_sensitive(streaming_proxy_auth_use, flac_cfg.stream.use_proxy); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use), flac_cfg.stream.proxy_use_auth); + gtk_signal_connect(GTK_OBJECT(streaming_proxy_auth_use), "clicked", GTK_SIGNAL_FUNC(proxy_auth_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_auth_use, FALSE, FALSE, 0); + + streaming_proxy_auth_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_proxy_auth_hbox, flac_cfg.stream.use_proxy && flac_cfg.stream.proxy_use_auth); + gtk_box_pack_start(GTK_BOX(streaming_proxy_vbox), streaming_proxy_auth_hbox, FALSE, FALSE, 0); + + streaming_proxy_auth_user_label = gtk_label_new(_("Username:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_user_label, FALSE, FALSE, 0); + + streaming_proxy_auth_user_entry = gtk_entry_new(); + if(flac_cfg.stream.proxy_user) + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_auth_user_entry), flac_cfg.stream.proxy_user); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_user_entry, TRUE, TRUE, 0); + + streaming_proxy_auth_pass_label = gtk_label_new(_("Password:")); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_pass_label, FALSE, FALSE, 0); + + streaming_proxy_auth_pass_entry = gtk_entry_new(); + if(flac_cfg.stream.proxy_pass) + gtk_entry_set_text(GTK_ENTRY(streaming_proxy_auth_pass_entry), flac_cfg.stream.proxy_pass); + gtk_entry_set_visibility(GTK_ENTRY(streaming_proxy_auth_pass_entry), FALSE); + gtk_box_pack_start(GTK_BOX(streaming_proxy_auth_hbox), streaming_proxy_auth_pass_entry, TRUE, TRUE, 0); + + + /* + * Save to disk config. + */ + streaming_save_frame = gtk_frame_new(_("Save stream to disk:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_save_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_save_frame, FALSE, FALSE, 0); + + streaming_save_vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(streaming_save_vbox), 5); + gtk_container_add(GTK_CONTAINER(streaming_save_frame), streaming_save_vbox); + + streaming_save_use = gtk_check_button_new_with_label(_("Save stream to disk")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_save_use), flac_cfg.stream.save_http_stream); + gtk_signal_connect(GTK_OBJECT(streaming_save_use), "clicked", GTK_SIGNAL_FUNC(streaming_save_use_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_use, FALSE, FALSE, 0); + + streaming_save_hbox = gtk_hbox_new(FALSE, 5); + gtk_widget_set_sensitive(streaming_save_hbox, flac_cfg.stream.save_http_stream); + gtk_box_pack_start(GTK_BOX(streaming_save_vbox), streaming_save_hbox, FALSE, FALSE, 0); + + streaming_save_label = gtk_label_new(_("Path:")); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_label, FALSE, FALSE, 0); + + streaming_save_entry = gtk_entry_new(); + gtk_entry_set_text(GTK_ENTRY(streaming_save_entry), flac_cfg.stream.save_http_path); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_entry, TRUE, TRUE, 0); + + streaming_save_browse = gtk_button_new_with_label(_("Browse")); + gtk_signal_connect(GTK_OBJECT(streaming_save_browse), "clicked", GTK_SIGNAL_FUNC(streaming_save_browse_cb), NULL); + gtk_box_pack_start(GTK_BOX(streaming_save_hbox), streaming_save_browse, FALSE, FALSE, 0); + +#ifdef FLAC_ICECAST + streaming_cast_frame = gtk_frame_new(_("SHOUT/Icecast:")); + gtk_container_set_border_width(GTK_CONTAINER(streaming_cast_frame), 5); + gtk_box_pack_start(GTK_BOX(streaming_vbox), streaming_cast_frame, FALSE, FALSE, 0); + + streaming_cast_vbox = gtk_vbox_new(5, FALSE); + gtk_container_add(GTK_CONTAINER(streaming_cast_frame), streaming_cast_vbox); + + streaming_cast_title = gtk_check_button_new_with_label(_("Enable SHOUT/Icecast title streaming")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_cast_title), flac_cfg.stream.cast_title_streaming); + gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_cast_title, FALSE, FALSE, 0); + + streaming_udp_title = gtk_check_button_new_with_label(_("Enable Icecast Metadata UDP Channel")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(streaming_udp_title), flac_cfg.stream.use_udp_channel); + gtk_box_pack_start(GTK_BOX(streaming_cast_vbox), streaming_udp_title, FALSE, FALSE, 0); +#endif + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), streaming_vbox, gtk_label_new(_("Streaming"))); + + /* Buttons */ + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + ok = gtk_button_new_with_label(_("Ok")); + gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(flac_configurewin_ok), NULL); + GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0); + gtk_widget_grab_default(ok); + + cancel = gtk_button_new_with_label(_("Cancel")); + gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(flac_configurewin)); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + gtk_widget_show_all(flac_configurewin); +} + +void FLAC_XMMS__aboutbox() +{ + static GtkWidget *about_window; + + if (about_window) + gdk_window_raise(about_window->window); + + about_window = xmms_show_message( + _("About Flac Plugin"), + _("Flac Plugin by Josh Coalson\n" + "contributions by\n" + "......\n" + "......\n" + "and\n" + "Daisuke Shimamura\n" + "Visit http://flac.sourceforge.net/"), + _("Ok"), FALSE, NULL, NULL); + gtk_signal_connect(GTK_OBJECT(about_window), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &about_window); +} + +/* + * Get text of an Entry or a ComboBox + */ +static gchar *gtk_entry_get_text_1 (GtkWidget *widget) +{ + if (GTK_IS_COMBO(widget)) + { + return gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(widget)->entry)); + }else if (GTK_IS_ENTRY(widget)) + { + return gtk_entry_get_text(GTK_ENTRY(widget)); + }else + { + return NULL; + } +} --- src/plugin_bmp/configure.h-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/configure.h 2005-05-25 15:39:29.000000000 +0200 @@ -0,0 +1,77 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Based on mpg123 plugin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __CONFIGURE_H__ +#define __CONFIGURE_H__ + +#include + +typedef struct { + struct { + gboolean tag_override; + gchar *tag_format; + gboolean convert_char_set; + gchar *user_char_set; + } title; + + struct { + gint http_buffer_size; + gint http_prebuffer; + gboolean use_proxy; + gchar *proxy_host; + gint proxy_port; + gboolean proxy_use_auth; + gchar *proxy_user; + gchar *proxy_pass; + gboolean save_http_stream; + gchar *save_http_path; + gboolean cast_title_streaming; + gboolean use_udp_channel; + } stream; + + struct { + struct { + gboolean enable; + gboolean album_mode; + gint preamp; + gboolean hard_limit; + } replaygain; + struct { + struct { + gboolean dither_24_to_16; + } normal; + struct { + gboolean dither; + gint noise_shaping; /* value must be one of NoiseShaping enum, c.f. plugin_common/replaygain_synthesis.h */ + gint bps_out; + } replaygain; + } resolution; + } output; +} flac_config_t; + +extern flac_config_t flac_cfg; + +extern void FLAC_XMMS__configure(void); +extern void FLAC_XMMS__aboutbox(void); + +#endif + + + --- src/plugin_bmp/tag.c-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/tag.c 2005-05-25 15:41:23.000000000 +0200 @@ -0,0 +1,150 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Based on FLAC plugin.c and mpg123 plugin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FLAC/metadata.h" +#include "plugin_common/tags.h" +#include "charset.h" +#include "configure.h" + +/* + * Function local__extname (filename) + * + * Return pointer within filename to its extenstion, or NULL if + * filename has no extension. + * + */ +static char *local__extname(const char *filename) +{ + char *ext = strrchr(filename, '.'); + + if (ext != NULL) + ++ext; + + return ext; +} + +static char *local__getstr(char* str) +{ + if (str && strlen(str) > 0) + return str; + return NULL; +} + +static int local__getnum(char* str) +{ + if (str && strlen(str) > 0) + return atoi(str); + return 0; +} + +static char *local__getfield(const FLAC__StreamMetadata *tags, const char *name) +{ + if (0 != tags) { + const char *utf8 = FLAC_plugin__tags_get_tag_utf8(tags, name); + if (0 != utf8) { + if(flac_cfg.title.convert_char_set) + return convert_from_utf8_to_user(utf8); + else + return strdup(utf8); + } + } + + return 0; +} + +static void local__safe_free(char *s) +{ + if (0 != s) + free(s); +} + +/* + * Function flac_format_song_title (tag, filename) + * + * Create song title according to `tag' and/or `filename' and + * return it. The title must be subsequently freed using g_free(). + * + */ +char *flac_format_song_title(char *filename) +{ + char *ret = NULL; + TitleInput *input = NULL; + FLAC__StreamMetadata *tags; + char *title, *artist, *performer, *album, *date, *tracknumber, *genre, *description; + + FLAC_plugin__tags_get(filename, &tags); + + title = local__getfield(tags, "TITLE"); + artist = local__getfield(tags, "ARTIST"); + performer = local__getfield(tags, "PERFORMER"); + album = local__getfield(tags, "ALBUM"); + date = local__getfield(tags, "DATE"); + tracknumber = local__getfield(tags, "TRACKNUMBER"); + genre = local__getfield(tags, "GENRE"); + description = local__getfield(tags, "DESCRIPTION"); + + XMMS_NEW_TITLEINPUT(input); + + input->performer = local__getstr(performer); + if(!input->performer) + input->performer = local__getstr(artist); + input->album_name = local__getstr(album); + input->track_name = local__getstr(title); + input->track_number = local__getnum(tracknumber); + input->year = local__getnum(date); + input->genre = local__getstr(genre); + input->comment = local__getstr(description); + + input->file_name = g_basename(filename); + input->file_path = filename; + input->file_ext = local__extname(filename); + ret = xmms_get_titlestring(flac_cfg.title.tag_override ? flac_cfg.title.tag_format : xmms_get_gentitle_format(), input); + g_free(input); + + if (!ret) { + /* + * Format according to filename. + */ + ret = g_strdup(g_basename(filename)); + if (local__extname(ret) != NULL) + *(local__extname(ret) - 1) = '\0'; /* removes period */ + } + + FLAC_plugin__tags_destroy(&tags); + local__safe_free(title); + local__safe_free(artist); + local__safe_free(performer); + local__safe_free(album); + local__safe_free(date); + local__safe_free(tracknumber); + local__safe_free(genre); + local__safe_free(description); + return ret; +} --- src/plugin_bmp/tag.h-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/tag.h 2005-05-25 15:36:41.000000000 +0200 @@ -0,0 +1,24 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004 Daisuke Shimamura + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FLAC__PLUGIN_XMMS__TAG_H__ +#define __FLAC__PLUGIN_XMMS__TAG_H__ + +gchar *flac_format_song_title(gchar * filename); + +#endif --- src/plugin_bmp/charset.c-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/charset.c 2005-05-25 15:38:07.000000000 +0200 @@ -0,0 +1,194 @@ +/* libxmms-flac - XMMS FLAC input plugin + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * Almost from charset.c + * EasyTAG - Tag editor for MP3 and OGG files + * Copyright (C) 1999-2001 Håvard Kvålen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "plugin_common/charset.h" +#include "plugin_common/locale_hack.h" +#include "charset.h" +#include "configure.h" + + +/**************** + * Declarations * + ****************/ + +#define CHARSET_TRANS_ARRAY_LEN ( sizeof(charset_trans_array) / sizeof((charset_trans_array)[0]) ) +const CharsetInfo charset_trans_array[] = { + {N_("Arabic (IBM-864)"), "IBM864" }, + {N_("Arabic (ISO-8859-6)"), "ISO-8859-6" }, + {N_("Arabic (Windows-1256)"), "windows-1256" }, + {N_("Baltic (ISO-8859-13)"), "ISO-8859-13" }, + {N_("Baltic (ISO-8859-4)"), "ISO-8859-4" }, + {N_("Baltic (Windows-1257)"), "windows-1257" }, + {N_("Celtic (ISO-8859-14)"), "ISO-8859-14" }, + {N_("Central European (IBM-852)"), "IBM852" }, + {N_("Central European (ISO-8859-2)"), "ISO-8859-2" }, + {N_("Central European (Windows-1250)"), "windows-1250" }, + {N_("Chinese Simplified (GB18030)"), "gb18030" }, + {N_("Chinese Simplified (GB2312)"), "GB2312" }, + {N_("Chinese Traditional (Big5)"), "Big5" }, + {N_("Chinese Traditional (Big5-HKSCS)"), "Big5-HKSCS" }, + {N_("Cyrillic (IBM-855)"), "IBM855" }, + {N_("Cyrillic (ISO-8859-5)"), "ISO-8859-5" }, + {N_("Cyrillic (ISO-IR-111)"), "ISO-IR-111" }, + {N_("Cyrillic (KOI8-R)"), "KOI8-R" }, + {N_("Cyrillic (Windows-1251)"), "windows-1251" }, + {N_("Cyrillic/Russian (CP-866)"), "IBM866" }, + {N_("Cyrillic/Ukrainian (KOI8-U)"), "KOI8-U" }, + {N_("English (US-ASCII)"), "us-ascii" }, + {N_("Greek (ISO-8859-7)"), "ISO-8859-7" }, + {N_("Greek (Windows-1253)"), "windows-1253" }, + {N_("Hebrew (IBM-862)"), "IBM862" }, + {N_("Hebrew (Windows-1255)"), "windows-1255" }, + {N_("Japanese (EUC-JP)"), "EUC-JP" }, + {N_("Japanese (ISO-2022-JP)"), "ISO-2022-JP" }, + {N_("Japanese (Shift_JIS)"), "Shift_JIS" }, + {N_("Korean (EUC-KR)"), "EUC-KR" }, + {N_("Nordic (ISO-8859-10)"), "ISO-8859-10" }, + {N_("South European (ISO-8859-3)"), "ISO-8859-3" }, + {N_("Thai (TIS-620)"), "TIS-620" }, + {N_("Turkish (IBM-857)"), "IBM857" }, + {N_("Turkish (ISO-8859-9)"), "ISO-8859-9" }, + {N_("Turkish (Windows-1254)"), "windows-1254" }, + {N_("Unicode (UTF-7)"), "UTF-7" }, + {N_("Unicode (UTF-8)"), "UTF-8" }, + {N_("Unicode (UTF-16BE)"), "UTF-16BE" }, + {N_("Unicode (UTF-16LE)"), "UTF-16LE" }, + {N_("Unicode (UTF-32BE)"), "UTF-32BE" }, + {N_("Unicode (UTF-32LE)"), "UTF-32LE" }, + {N_("Vietnamese (VISCII)"), "VISCII" }, + {N_("Vietnamese (Windows-1258)"), "windows-1258" }, + {N_("Visual Hebrew (ISO-8859-8)"), "ISO-8859-8" }, + {N_("Western (IBM-850)"), "IBM850" }, + {N_("Western (ISO-8859-1)"), "ISO-8859-1" }, + {N_("Western (ISO-8859-15)"), "ISO-8859-15" }, + {N_("Western (Windows-1252)"), "windows-1252" } + + /* + * From this point, character sets aren't supported by iconv + */ +#if 0 + {N_("Arabic (IBM-864-I)"), "IBM864i" }, + {N_("Arabic (ISO-8859-6-E)"), "ISO-8859-6-E" }, + {N_("Arabic (ISO-8859-6-I)"), "ISO-8859-6-I" }, + {N_("Arabic (MacArabic)"), "x-mac-arabic" }, + {N_("Armenian (ARMSCII-8)"), "armscii-8" }, + {N_("Central European (MacCE)"), "x-mac-ce" }, + {N_("Chinese Simplified (GBK)"), "x-gbk" }, + {N_("Chinese Simplified (HZ)"), "HZ-GB-2312" }, + {N_("Chinese Traditional (EUC-TW)"), "x-euc-tw" }, + {N_("Croatian (MacCroatian)"), "x-mac-croatian" }, + {N_("Cyrillic (MacCyrillic)"), "x-mac-cyrillic" }, + {N_("Cyrillic/Ukrainian (MacUkrainian)"), "x-mac-ukrainian" }, + {N_("Farsi (MacFarsi)"), "x-mac-farsi"}, + {N_("Greek (MacGreek)"), "x-mac-greek" }, + {N_("Gujarati (MacGujarati)"), "x-mac-gujarati" }, + {N_("Gurmukhi (MacGurmukhi)"), "x-mac-gurmukhi" }, + {N_("Hebrew (ISO-8859-8-E)"), "ISO-8859-8-E" }, + {N_("Hebrew (ISO-8859-8-I)"), "ISO-8859-8-I" }, + {N_("Hebrew (MacHebrew)"), "x-mac-hebrew" }, + {N_("Hindi (MacDevanagari)"), "x-mac-devanagari" }, + {N_("Icelandic (MacIcelandic)"), "x-mac-icelandic" }, + {N_("Korean (JOHAB)"), "x-johab" }, + {N_("Korean (UHC)"), "x-windows-949" }, + {N_("Romanian (MacRomanian)"), "x-mac-romanian" }, + {N_("Turkish (MacTurkish)"), "x-mac-turkish" }, + {N_("User Defined"), "x-user-defined" }, + {N_("Vietnamese (TCVN)"), "x-viet-tcvn5712" }, + {N_("Vietnamese (VPS)"), "x-viet-vps" }, + {N_("Western (MacRoman)"), "x-mac-roman" }, + /* charsets whithout posibly translatable names */ + {"T61.8bit", "T61.8bit" }, + {"x-imap4-modified-utf7", "x-imap4-modified-utf7"}, + {"x-u-escaped", "x-u-escaped" }, + {"windows-936", "windows-936" } +#endif +}; + +/************* + * Functions * + *************/ + +/* + * Commons conversion functions + */ +char *convert_from_utf8_to_user(const char *string) +{ + return FLAC_plugin__charset_convert_string(string, "UTF-8", flac_cfg.title.user_char_set); +} + +char *convert_from_user_to_utf8(const char *string) +{ + return FLAC_plugin__charset_convert_string(string, flac_cfg.title.user_char_set, "UTF-8"); +} + +GList *Charset_Create_List (void) +{ + GList *list = NULL; + guint i; + + for (i=0; i + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef __CHARSET_H__ +#define __CHARSET_H__ + + +/*************** + * Declaration * + ***************/ + +typedef struct { + gchar *charset_title; + gchar *charset_name; +} CharsetInfo; + +/* translated charset titles */ +extern const CharsetInfo charset_trans_array[]; + +/************** + * Prototypes * + **************/ + +/* + * The returned strings are malloc()ed an must be free()d by the caller + */ +char *convert_from_utf8_to_user(const char *string); +char *convert_from_user_to_utf8(const char *string); + +GList *Charset_Create_List (void); +GList *Charset_Create_List_UTF8_Only (void); +gchar *Charset_Get_Name_From_Title (gchar *charset_title); +gchar *Charset_Get_Title_From_Name (gchar *charset_name); + +#endif /* __CHARSET_H__ */ + --- src/plugin_bmp/http.c-dist 2005-05-25 15:45:58.000000000 +0200 +++ src/plugin_bmp/http.c 2005-05-25 16:42:42.000000000 +0200 @@ -0,0 +1,896 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +/* modified for FLAC support by Steven Richman (2003) */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "configure.h" +#include "plugin_common/locale_hack.h" +#include "FLAC/format.h" +#include "plugin.h" + +#ifndef HAVE_SOCKLEN_T +typedef unsigned int socklen_t; +#endif + +#define min(x,y) ((x)<(y)?(x):(y)) +#define min3(x,y,z) (min(x,y)<(z)?min(x,y):(z)) +#define min4(x,y,z,w) (min3(x,y,z)<(w)?min3(x,y,z):(w)) + +static gchar *icy_name = NULL; +static gint icy_metaint = 0; + +extern InputPlugin flac_ip; + +#undef DEBUG_UDP + +/* Static udp channel functions */ +static int udp_establish_listener (gint *sock); +static int udp_check_for_data(gint sock); + +static char *flac_http_get_title(char *url); + +static gboolean prebuffering, going, eof = FALSE; +static gint sock, rd_index, wr_index, buffer_length, prebuffer_length; +static guint64 buffer_read = 0; +static gchar *buffer; +static guint64 offset; +static pthread_t thread; +static GtkWidget *error_dialog = NULL; + +static FILE *output_file = NULL; + +#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3)) + +/* Encode the string S of length LENGTH to base64 format and place it + to STORE. STORE will be 0-terminated, and must point to a writable + buffer of at least 1+BASE64_LENGTH(length) bytes. */ +static void base64_encode (const gchar *s, gchar *store, gint length) +{ + /* Conversion table. */ + static gchar tbl[64] = { + 'A','B','C','D','E','F','G','H', + 'I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X', + 'Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3', + '4','5','6','7','8','9','+','/' + }; + gint i; + guchar *p = (guchar *)store; + + /* Transform the 3x8 bits to 4x6 bits, as required by base64. */ + for (i = 0; i < length; i += 3) + { + *p++ = tbl[s[0] >> 2]; + *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)]; + *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)]; + *p++ = tbl[s[2] & 0x3f]; + s += 3; + } + /* Pad the result if necessary... */ + if (i == length + 1) + *(p - 1) = '='; + else if (i == length + 2) + *(p - 1) = *(p - 2) = '='; + /* ...and zero-terminate it. */ + *p = '\0'; +} + +/* Create the authentication header contents for the `Basic' scheme. + This is done by encoding the string `USER:PASS' in base64 and + prepending `HEADER: Basic ' to it. */ +static gchar *basic_authentication_encode (const gchar *user, const gchar *passwd, const gchar *header) +{ + gchar *t1, *t2, *res; + gint len1 = strlen (user) + 1 + strlen (passwd); + gint len2 = BASE64_LENGTH (len1); + + t1 = g_strdup_printf("%s:%s", user, passwd); + t2 = g_malloc0(len2 + 1); + base64_encode (t1, t2, len1); + res = g_strdup_printf("%s: Basic %s\r\n", header, t2); + g_free(t2); + g_free(t1); + + return res; +} + +static void parse_url(const gchar * url, gchar ** user, gchar ** pass, gchar ** host, int *port, gchar ** filename) +{ + gchar *h, *p, *pt, *f, *temp, *ptr; + + temp = g_strdup(url); + ptr = temp; + + if (!strncasecmp("http://", ptr, 7)) + ptr += 7; + h = strchr(ptr, '@'); + f = strchr(ptr, '/'); + if (h != NULL && (!f || h < f)) + { + *h = '\0'; + p = strchr(ptr, ':'); + if (p != NULL && p < h) + { + *p = '\0'; + p++; + *pass = g_strdup(p); + } + else + *pass = NULL; + *user = g_strdup(ptr); + h++; + ptr = h; + } + else + { + *user = NULL; + *pass = NULL; + h = ptr; + } + pt = strchr(ptr, ':'); + if (pt != NULL && (f == NULL || pt < f)) + { + *pt = '\0'; + *port = atoi(pt + 1); + } + else + { + if (f) + *f = '\0'; + *port = 80; + } + *host = g_strdup(h); + + if (f) + *filename = g_strdup(f + 1); + else + *filename = NULL; + g_free(temp); +} + +void flac_http_close(void) +{ + going = FALSE; + + pthread_join(thread, NULL); + g_free(icy_name); + icy_name = NULL; +} + + +static gint http_used(void) +{ + if (wr_index >= rd_index) + return wr_index - rd_index; + return buffer_length - (rd_index - wr_index); +} + +static gint http_free(void) +{ + if (rd_index > wr_index) + return (rd_index - wr_index) - 1; + return (buffer_length - (wr_index - rd_index)) - 1; +} + +static void http_wait_for_data(gint bytes) +{ + while ((prebuffering || http_used() < bytes) && !eof && going) + xmms_usleep(10000); +} + +static void show_error_message(gchar *error) +{ + if(!error_dialog) + { + GDK_THREADS_ENTER(); + error_dialog = xmms_show_message(_("Error"), error, _("Ok"), FALSE, + NULL, NULL); + gtk_signal_connect(GTK_OBJECT(error_dialog), + "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &error_dialog); + GDK_THREADS_LEAVE(); + } +} + +int flac_http_read(gpointer data, gint length) +{ + gint len, cnt, off = 0, meta_len, meta_off = 0, i; + gchar *meta_data, **tags, *temp, *title; + if (length > buffer_length) { + length = buffer_length; + } + + http_wait_for_data(length); + + if (!going) + return 0; + len = min(http_used(), length); + + while (len && http_used()) + { + if ((flac_cfg.stream.cast_title_streaming) && (icy_metaint > 0) && (buffer_read % icy_metaint) == 0 && (buffer_read > 0)) + { + meta_len = *((guchar *) buffer + rd_index) * 16; + rd_index = (rd_index + 1) % buffer_length; + if (meta_len > 0) + { + http_wait_for_data(meta_len); + meta_data = g_malloc0(meta_len); + if (http_used() >= meta_len) + { + while (meta_len) + { + cnt = min(meta_len, buffer_length - rd_index); + memcpy(meta_data + meta_off, buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_length; + meta_len -= cnt; + meta_off += cnt; + } + tags = g_strsplit(meta_data, "';", 0); + + for (i = 0; tags[i]; i++) + { + if (!strncasecmp(tags[i], "StreamTitle=", 12)) + { + temp = g_strdup(tags[i] + 13); + title = g_strdup_printf("%s (%s)", temp, icy_name); + set_track_info(title, -1); + g_free(title); + g_free(temp); + } + + } + g_strfreev(tags); + + } + g_free(meta_data); + } + if (!http_used()) + http_wait_for_data(length - off); + cnt = min3(len, buffer_length - rd_index, http_used()); + } + else if ((icy_metaint > 0) && (flac_cfg.stream.cast_title_streaming)) + cnt = min4(len, buffer_length - rd_index, http_used(), icy_metaint - (gint) (buffer_read % icy_metaint)); + else + cnt = min3(len, buffer_length - rd_index, http_used()); + if (output_file) + fwrite(buffer + rd_index, 1, cnt, output_file); + + memcpy((gchar *)data + off, buffer + rd_index, cnt); + rd_index = (rd_index + cnt) % buffer_length; + buffer_read += cnt; + len -= cnt; + off += cnt; + } + if (!off) { + fprintf(stderr, "returning zero\n"); + } + return off; +} + +static gboolean http_check_for_data(void) +{ + + fd_set set; + struct timeval tv; + gint ret; + + tv.tv_sec = 0; + tv.tv_usec = 20000; + FD_ZERO(&set); + FD_SET(sock, &set); + ret = select(sock + 1, &set, NULL, NULL, &tv); + if (ret > 0) + return TRUE; + return FALSE; +} + +gint flac_http_read_line(gchar * buf, gint size) +{ + gint i = 0; + + while (going && i < size - 1) + { + if (http_check_for_data()) + { + if (read(sock, buf + i, 1) <= 0) + return -1; + if (buf[i] == '\n') + break; + if (buf[i] != '\r') + i++; + } + } + if (!going) + return -1; + buf[i] = '\0'; + return i; +} + +/* returns the file descriptor of the socket, or -1 on error */ +static int http_connect (gchar *url_, gboolean head, guint64 offset) +{ + gchar line[1024], *user, *pass, *host, *filename, + *status, *url, *temp, *file; + gchar *chost; + gint cnt, error, err_len, port, cport; + gboolean redirect; + int udp_sock = 0; + fd_set set; + struct hostent *hp; + struct sockaddr_in address; + struct timeval tv; + + url = g_strdup (url_); + + do + { + redirect=FALSE; + + g_strstrip(url); + + parse_url(url, &user, &pass, &host, &port, &filename); + + if ((!filename || !*filename) && url[strlen(url) - 1] != '/') + temp = g_strconcat(url, "/", NULL); + else + temp = g_strdup(url); + g_free(url); + url = temp; + + chost = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_host : host; + cport = flac_cfg.stream.use_proxy ? flac_cfg.stream.proxy_port : port; + + sock = socket(AF_INET, SOCK_STREAM, 0); + fcntl(sock, F_SETFL, O_NONBLOCK); + address.sin_family = AF_INET; + + status = g_strdup_printf(_("LOOKING UP %s"), chost); + flac_ip.set_info_text(status); + g_free(status); + + if (!(hp = gethostbyname(chost))) + { + status = g_strdup_printf(_("Couldn't look up host %s"), chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + } + + if (!eof) + { + memcpy(&address.sin_addr.s_addr, *(hp->h_addr_list), sizeof (address.sin_addr.s_addr)); + address.sin_port = (gint) g_htons(cport); + + status = g_strdup_printf(_("CONNECTING TO %s:%d"), chost, cport); + flac_ip.set_info_text(status); + g_free(status); + if (connect(sock, (struct sockaddr *) &address, sizeof (struct sockaddr_in)) == -1) + { + if (errno != EINPROGRESS) + { + status = g_strdup_printf(_("Couldn't connect to host %s"), chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + } + } + while (going) + { + tv.tv_sec = 0; + tv.tv_usec = 10000; + FD_ZERO(&set); + FD_SET(sock, &set); + if (select(sock + 1, NULL, &set, NULL, &tv) > 0) + { + err_len = sizeof (error); + getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &err_len); + if (error) + { + status = g_strdup_printf(_("Couldn't connect to host %s"), + chost); + show_error_message(status); + g_free(status); + + flac_ip.set_info_text(NULL); + eof = TRUE; + + } + break; + } + } + if (!eof) + { + gchar *auth = NULL, *proxy_auth = NULL; + gchar udpspace[30]; + int udp_port; + + if (flac_cfg.stream.use_udp_channel) + { + udp_port = udp_establish_listener (&udp_sock); + if (udp_port > 0) + sprintf (udpspace, "x-audiocast-udpport: %d\r\n", udp_port); + else + udp_sock = 0; + } + + if(user && pass) + auth = basic_authentication_encode(user, pass, "Authorization"); + + if (flac_cfg.stream.use_proxy) + { + file = g_strdup(url); + if(flac_cfg.stream.proxy_use_auth && flac_cfg.stream.proxy_user && flac_cfg.stream.proxy_pass) + { + proxy_auth = basic_authentication_encode(flac_cfg.stream.proxy_user, + flac_cfg.stream.proxy_pass, + "Proxy-Authorization"); + } + } + else + file = g_strconcat("/", filename, NULL); + + temp = g_strdup_printf("GET %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: %s/%s\r\n" + "%s%s%s%s", + file, host, "Reference FLAC Player", FLAC__VERSION_STRING, + proxy_auth ? proxy_auth : "", auth ? auth : "", + flac_cfg.stream.cast_title_streaming ? "Icy-MetaData:1\r\n" : "", + flac_cfg.stream.use_udp_channel ? udpspace : ""); + if (offset && !head) { + gchar *temp_dead = temp; + temp = g_strdup_printf("%sRange: %llu-\r\n", temp, offset); + fprintf (stderr, "%s", temp); + g_free (temp_dead); + } + + g_free(file); + if(proxy_auth) + g_free(proxy_auth); + if(auth) + g_free(auth); + write(sock, temp, strlen(temp)); + write(sock, "\r\n", 2); + g_free(temp); + flac_ip.set_info_text(_("CONNECTED: WAITING FOR REPLY")); + while (going && !eof) + { + if (http_check_for_data()) + { + if (flac_http_read_line(line, 1024)) + { + status = strchr(line, ' '); + if (status) + { + if (status[1] == '2') + break; + else if(status[1] == '3' && status[2] == '0' && status[3] == '2') + { + while(going) + { + if(http_check_for_data()) + { + if((cnt = flac_http_read_line(line, 1024)) != -1) + { + if(!cnt) + break; + if(!strncmp(line, "Location:", 9)) + { + g_free(url); + url = g_strdup(line+10); + } + } + else + { + eof=TRUE; + flac_ip.set_info_text(NULL); + break; + } + } + } + redirect=TRUE; + break; + } + else + { + status = g_strdup_printf(_("Couldn't connect to host %s\nServer reported: %s"), chost, status); + show_error_message(status); + g_free(status); + break; + } + } + } + else + { + eof = TRUE; + flac_ip.set_info_text(NULL); + } + } + } + + while (going && !redirect) + { + if (http_check_for_data()) + { + if ((cnt = flac_http_read_line(line, 1024)) != -1) + { + if (!cnt) + break; + if (!strncmp(line, "icy-name:", 9)) + icy_name = g_strdup(line + 9); + else if (!strncmp(line, "x-audiocast-name:", 17)) + icy_name = g_strdup(line + 17); + if (!strncmp(line, "icy-metaint:", 12)) + icy_metaint = atoi(line + 12); + if (!strncmp(line, "x-audiocast-udpport:", 20)) { +#ifdef DEBUG_UDP + fprintf (stderr, "Server wants udp messages on port %d\n", atoi (line + 20)); +#endif + /*udp_serverport = atoi (line + 20);*/ + } + + } + else + { + eof = TRUE; + flac_ip.set_info_text(NULL); + break; + } + } + } + } + } + + if(redirect) + { + if (output_file) + { + fclose(output_file); + output_file = NULL; + } + close(sock); + } + + g_free(user); + g_free(pass); + g_free(host); + g_free(filename); + } while(redirect); + + g_free(url); + return eof ? -1 : sock; +} + +static void *http_buffer_loop(void *arg) +{ + gchar *status, *url, *temp, *file; + gint cnt, written; + int udp_sock = 0; + + url = (gchar *) arg; + sock = http_connect (url, false, offset); + + if (sock >= 0 && flac_cfg.stream.save_http_stream) { + gchar *output_name; + file = flac_http_get_title(url); + output_name = file; + if (!strncasecmp(output_name, "http://", 7)) + output_name += 7; + temp = strrchr(output_name, '.'); + if (temp && (!strcasecmp(temp, ".fla") || !strcasecmp(temp, ".flac"))) + *temp = '\0'; + + while ((temp = strchr(output_name, '/'))) + *temp = '_'; + output_name = g_strdup_printf("%s/%s.flac", flac_cfg.stream.save_http_path, output_name); + + g_free(file); + + output_file = fopen(output_name, "wb"); + g_free(output_name); + } + + while (going) + { + + if (!http_used() && !flac_ip.output->buffer_playing()) + prebuffering = TRUE; + if (http_free() > 0 && !eof) + { + if (http_check_for_data()) + { + cnt = min(http_free(), buffer_length - wr_index); + if (cnt > 1024) + cnt = 1024; + written = read(sock, buffer + wr_index, cnt); + if (written <= 0) + { + eof = TRUE; + if (prebuffering) + { + prebuffering = FALSE; + + flac_ip.set_info_text(NULL); + } + + } + else + wr_index = (wr_index + written) % buffer_length; + } + + if (prebuffering) + { + if (http_used() > prebuffer_length) + { + prebuffering = FALSE; + flac_ip.set_info_text(NULL); + } + else + { + status = g_strdup_printf(_("PRE-BUFFERING: %dKB/%dKB"), http_used() / 1024, prebuffer_length / 1024); + flac_ip.set_info_text(status); + g_free(status); + } + + } + } + else + xmms_usleep(10000); + + if (flac_cfg.stream.use_udp_channel && udp_sock != 0) + if (udp_check_for_data(udp_sock) < 0) + { + close(udp_sock); + udp_sock = 0; + } + } + if (output_file) + { + fclose(output_file); + output_file = NULL; + } + if (sock >= 0) { + close(sock); + } + if (udp_sock != 0) + close(udp_sock); + + g_free(buffer); + g_free(url); + + pthread_exit(NULL); + return NULL; /* avoid compiler warning */ +} + +int flac_http_open(gchar * _url, guint64 _offset) +{ + gchar *url; + + url = g_strdup(_url); + + rd_index = 0; + wr_index = 0; + buffer_length = flac_cfg.stream.http_buffer_size * 1024; + prebuffer_length = (buffer_length * flac_cfg.stream.http_prebuffer) / 100; + buffer_read = 0; + icy_metaint = 0; + prebuffering = TRUE; + going = TRUE; + eof = FALSE; + buffer = g_malloc(buffer_length); + offset = _offset; + + pthread_create(&thread, NULL, http_buffer_loop, url); + + return 0; +} + +char *flac_http_get_title(char *url) +{ + if (icy_name) + return g_strdup(icy_name); + if (g_basename(url) && strlen(g_basename(url)) > 0) + return g_strdup(g_basename(url)); + return g_strdup(url); +} + +/* Start UDP Channel specific stuff */ + +/* Find a good local udp port and bind udp_sock to it, return the port */ +static int udp_establish_listener(int *sock) +{ + struct sockaddr_in sin; + socklen_t sinlen = sizeof (struct sockaddr_in); + +#ifdef DEBUG_UDP + fprintf (stderr,"Establishing udp listener\n"); +#endif + + if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): unable to create socket"); + return -1; + } + + memset(&sin, 0, sinlen); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = g_htonl(INADDR_ANY); + + if (bind(*sock, (struct sockaddr *)&sin, sinlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to bind socket to localhost: %s", strerror(errno)); + close(*sock); + return -1; + } + if (fcntl(*sock, F_SETFL, O_NONBLOCK) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to set flags: %s", strerror(errno)); + close(*sock); + return -1; + } + + memset(&sin, 0, sinlen); + if (getsockname(*sock, (struct sockaddr *)&sin, &sinlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_establish_listener(): Failed to retrieve socket info: %s", strerror(errno)); + close(*sock); + return -1; + } + +#ifdef DEBUG_UDP + fprintf (stderr,"Listening on local %s:%d\n", inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); +#endif + + return g_ntohs(sin.sin_port); +} + +static int udp_check_for_data(int sock) +{ + char buf[1025], **lines; + char *valptr; + gchar *title; + gint len, i; + struct sockaddr_in from; + socklen_t fromlen; + + fromlen = sizeof(struct sockaddr_in); + + if ((len = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen)) < 0) + { + if (errno != EAGAIN) + { + g_log(NULL, G_LOG_LEVEL_CRITICAL, + "udp_read_data(): Error reading from socket: %s", strerror(errno)); + return -1; + } + return 0; + } + buf[len] = '\0'; +#ifdef DEBUG_UDP + fprintf (stderr,"Received: [%s]\n", buf); +#endif + lines = g_strsplit(buf, "\n", 0); + if (!lines) + return 0; + + for (i = 0; lines[i]; i++) + { + while ((lines[i][strlen(lines[i]) - 1] == '\n') || + (lines[i][strlen(lines[i]) - 1] == '\r')) + lines[i][strlen(lines[i]) - 1] = '\0'; + + valptr = strchr(lines[i], ':'); + + if (!valptr) + continue; + else + valptr++; + + g_strstrip(valptr); + if (!strlen(valptr)) + continue; + + if (strstr(lines[i], "x-audiocast-streamtitle") != NULL) + { + title = g_strdup_printf ("%s (%s)", valptr, icy_name); + if (going) + set_track_info(title, -1); + g_free (title); + } + +#if 0 + else if (strstr(lines[i], "x-audiocast-streamlength") != NULL) + { + if (atoi(valptr) != -1) + set_track_info(NULL, atoi(valptr)); + } +#endif + + else if (strstr(lines[i], "x-audiocast-streammsg") != NULL) + { + /* set_track_info(title, -1); */ +/* xmms_show_message(_("Message"), valptr, _("Ok"), */ +/* FALSE, NULL, NULL); */ + g_message("Stream_message: %s", valptr); + } + +#if 0 + /* Use this to direct your webbrowser.. yeah right.. */ + else if (strstr(lines[i], "x-audiocast-streamurl") != NULL) + { + if (lasturl && g_strcmp (valptr, lasturl)) + { + c_message (stderr, "Song URL: %s\n", valptr); + g_free (lasturl); + lasturl = g_strdup (valptr); + } + } +#endif + else if (strstr(lines[i], "x-audiocast-udpseqnr:") != NULL) + { + gchar obuf[60]; + sprintf(obuf, "x-audiocast-ack: %ld \r\n", atol(valptr)); + if (sendto(sock, obuf, strlen(obuf), 0, (struct sockaddr *) &from, fromlen) < 0) + { + g_log(NULL, G_LOG_LEVEL_WARNING, + "udp_check_for_data(): Unable to send ack to server: %s", strerror(errno)); + } +#ifdef DEBUG_UDP + else + fprintf(stderr,"Sent ack: %s", obuf); + fprintf (stderr,"Remote: %s:%d\n", inet_ntoa(from.sin_addr), g_ntohs(from.sin_port)); +#endif + } + } + g_strfreev(lines); + return 0; +} --- src/plugin_bmp/http.h-dist 2005-05-25 15:46:00.000000000 +0200 +++ src/plugin_bmp/http.h 2005-05-25 15:40:12.000000000 +0200 @@ -0,0 +1,26 @@ +/* libxmms-flac - XMMS FLAC input plugin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __HTTP_H__ +#define __HTTP_H__ + +extern int flac_http_open(gchar * url, guint64 offset); +extern void flac_http_close(void); +extern int flac_http_read(gpointer data, gint length); + + +#endif --- src/plugin_bmp/Makefile.am-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/Makefile.am 2005-05-25 15:51:14.000000000 +0200 @@ -0,0 +1,65 @@ +# libxmms-flac - XMMS FLAC input plugin +# Copyright (C) 2000,2001,2002,2003,2004,2005 Josh Coalson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# +# GNU makefile +# + +noinst_HEADERS = \ + charset.h \ + configure.h \ + http.h \ + plugin.h \ + tag.h + +AM_CFLAGS = @BMP_CFLAGS@ + +INCLUDES = -I$(top_srcdir)/src +# if FLaC__INSTALL_BMP_PLUGIN_LOCALLY +# bmpinputplugindir = $(HOME)/.bmp/Plugins +# else +bmpinputplugindir = @BMP_INPUT_PLUGIN_DIR@ +# endif + +# Don't build a static library +LIBTOOL = $(top_builddir)/libtool-disable-static + +bmpinputplugin_LTLIBRARIES = libbmp-flac.la + +plugin_sources = charset.c configure.c fileinfo.c http.c plugin.c tag.c + +libbmp_flac_la_SOURCES = $(plugin_sources) + +# work around the bug in libtool where its relinking fails with a different DESTDIR +# for libtool bug info see: +# http://mail.gnu.org/pipermail/bug-libtool/2002-February/003018.html +# http://mail.gnu.org/pipermail/libtool/2002-April/006244.html +# http://mail.gnu.org/pipermail/libtool/2002-April/006250.html +# for fix info see: +# http://lists.freshrpms.net/pipermail/rpm-list/2002-April/000746.html +# the workaround is the extra '-L$(top_builddir)/src/libFLAC/.libs' +libbmp_flac_la_LIBADD = \ + $(top_builddir)/src/plugin_common/libplugin_common.la \ + $(top_builddir)/src/share/grabbag/libgrabbag.la \ + $(top_builddir)/src/share/replaygain_analysis/libreplaygain_analysis.la \ + $(top_builddir)/src/share/replaygain_synthesis/libreplaygain_synthesis.la \ + $(top_builddir)/src/share/utf8/libutf8.la \ + $(top_builddir)/src/libFLAC/libFLAC.la \ + -L$(top_builddir)/src/libFLAC/.libs \ + @BMP_LIBS@ \ + @LIBICONV@ +libbmp_flac_la_LDFLAGS = -module -avoid-version --- src/plugin_bmp/fileinfo.c-dist 2005-05-25 15:36:41.000000000 +0200 +++ src/plugin_bmp/fileinfo.c 2005-05-25 16:08:06.000000000 +0200 @@ -0,0 +1,424 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * Copyright (C) 1999,2000 Håvard Kvålen + * Copyright (C) 2002,2003,2004,2005 Daisuke Shimamura + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include /* for strlen() */ +#include +#include +#include +#include +#include + +#include "FLAC/metadata.h" +#include "charset.h" +#include "configure.h" +#include "plugin_common/tags.h" +#include "plugin_common/locale_hack.h" + +static GtkWidget *window = NULL; +static GList *genre_list = NULL; +static GtkWidget *filename_entry, *tag_frame; +static GtkWidget *title_entry, *artist_entry, *album_entry, *date_entry, *tracknum_entry, *comment_entry; +static GtkWidget *genre_combo; +static GtkWidget *flac_samplerate, *flac_channels, *flac_bits_per_sample, *flac_blocksize, *flac_filesize, *flac_samples, *flac_bitrate; + +static gchar *current_filename = NULL; +static FLAC__StreamMetadata *tags_ = NULL; + +static const gchar *vorbis_genres[] = +{ + N_("Blues"), N_("Classic Rock"), N_("Country"), N_("Dance"), + N_("Disco"), N_("Funk"), N_("Grunge"), N_("Hip-Hop"), + N_("Jazz"), N_("Metal"), N_("New Age"), N_("Oldies"), + N_("Other"), N_("Pop"), N_("R&B"), N_("Rap"), N_("Reggae"), + N_("Rock"), N_("Techno"), N_("Industrial"), N_("Alternative"), + N_("Ska"), N_("Death Metal"), N_("Pranks"), N_("Soundtrack"), + N_("Euro-Techno"), N_("Ambient"), N_("Trip-Hop"), N_("Vocal"), + N_("Jazz+Funk"), N_("Fusion"), N_("Trance"), N_("Classical"), + N_("Instrumental"), N_("Acid"), N_("House"), N_("Game"), + N_("Sound Clip"), N_("Gospel"), N_("Noise"), N_("Alt"), + N_("Bass"), N_("Soul"), N_("Punk"), N_("Space"), + N_("Meditative"), N_("Instrumental Pop"), + N_("Instrumental Rock"), N_("Ethnic"), N_("Gothic"), + N_("Darkwave"), N_("Techno-Industrial"), N_("Electronic"), + N_("Pop-Folk"), N_("Eurodance"), N_("Dream"), + N_("Southern Rock"), N_("Comedy"), N_("Cult"), + N_("Gangsta Rap"), N_("Top 40"), N_("Christian Rap"), + N_("Pop/Funk"), N_("Jungle"), N_("Native American"), + N_("Cabaret"), N_("New Wave"), N_("Psychedelic"), N_("Rave"), + N_("Showtunes"), N_("Trailer"), N_("Lo-Fi"), N_("Tribal"), + N_("Acid Punk"), N_("Acid Jazz"), N_("Polka"), N_("Retro"), + N_("Musical"), N_("Rock & Roll"), N_("Hard Rock"), N_("Folk"), + N_("Folk/Rock"), N_("National Folk"), N_("Swing"), + N_("Fast-Fusion"), N_("Bebob"), N_("Latin"), N_("Revival"), + N_("Celtic"), N_("Bluegrass"), N_("Avantgarde"), + N_("Gothic Rock"), N_("Progressive Rock"), + N_("Psychedelic Rock"), N_("Symphonic Rock"), N_("Slow Rock"), + N_("Big Band"), N_("Chorus"), N_("Easy Listening"), + N_("Acoustic"), N_("Humour"), N_("Speech"), N_("Chanson"), + N_("Opera"), N_("Chamber Music"), N_("Sonata"), N_("Symphony"), + N_("Booty Bass"), N_("Primus"), N_("Porn Groove"), + N_("Satire"), N_("Slow Jam"), N_("Club"), N_("Tango"), + N_("Samba"), N_("Folklore"), N_("Ballad"), N_("Power Ballad"), + N_("Rhythmic Soul"), N_("Freestyle"), N_("Duet"), + N_("Punk Rock"), N_("Drum Solo"), N_("A Cappella"), + N_("Euro-House"), N_("Dance Hall"), N_("Goa"), + N_("Drum & Bass"), N_("Club-House"), N_("Hardcore"), + N_("Terror"), N_("Indie"), N_("BritPop"), N_("Negerpunk"), + N_("Polsk Punk"), N_("Beat"), N_("Christian Gangsta Rap"), + N_("Heavy Metal"), N_("Black Metal"), N_("Crossover"), + N_("Contemporary Christian"), N_("Christian Rock"), + N_("Merengue"), N_("Salsa"), N_("Thrash Metal"), + N_("Anime"), N_("JPop"), N_("Synthpop") +}; + +static void label_set_text(GtkWidget * label, char *str, ...) +{ + va_list args; + gchar *tempstr; + + va_start(args, str); + tempstr = g_strdup_vprintf(str, args); + va_end(args); + + gtk_label_set_text(GTK_LABEL(label), tempstr); + g_free(tempstr); +} + +static void set_entry_tag(GtkEntry * entry, const char * utf8) +{ + if(utf8) { + if(flac_cfg.title.convert_char_set) { + char *text = convert_from_utf8_to_user(utf8); + gtk_entry_set_text(entry, text); + free(text); + } + else + gtk_entry_set_text(entry, utf8); + } + else + gtk_entry_set_text(entry, ""); +} + +static void get_entry_tag(GtkEntry * entry, const char *name) +{ + gchar *text; + char *utf8; + + text = gtk_entry_get_text(entry); + if (!text || strlen(text) == 0) + return; + if(flac_cfg.title.convert_char_set) + utf8 = convert_from_user_to_utf8(text); + else + utf8 = text; + + FLAC_plugin__tags_add_tag_utf8(tags_, name, utf8, /*separator=*/0); + + if(flac_cfg.title.convert_char_set) + free(utf8); +} + +static void show_tag(void) +{ + set_entry_tag(GTK_ENTRY(title_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "TITLE")); + set_entry_tag(GTK_ENTRY(artist_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "ARTIST")); + set_entry_tag(GTK_ENTRY(album_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "ALBUM")); + set_entry_tag(GTK_ENTRY(date_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "DATE")); + set_entry_tag(GTK_ENTRY(tracknum_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "TRACKNUMBER")); + set_entry_tag(GTK_ENTRY(comment_entry) , FLAC_plugin__tags_get_tag_utf8(tags_, "DESCRIPTION")); + set_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), FLAC_plugin__tags_get_tag_utf8(tags_, "GENRE")); +} + +static void save_tag(GtkWidget * w, gpointer data) +{ + (void)w; + (void)data; + + FLAC_plugin__tags_delete_tag(tags_, "TITLE"); + FLAC_plugin__tags_delete_tag(tags_, "ARTIST"); + FLAC_plugin__tags_delete_tag(tags_, "ALBUM"); + FLAC_plugin__tags_delete_tag(tags_, "DATE"); + FLAC_plugin__tags_delete_tag(tags_, "TRACKNUMBER"); + FLAC_plugin__tags_delete_tag(tags_, "DESCRIPTION"); + FLAC_plugin__tags_delete_tag(tags_, "GENRE"); + + get_entry_tag(GTK_ENTRY(title_entry) , "TITLE"); + get_entry_tag(GTK_ENTRY(artist_entry) , "ARTIST"); + get_entry_tag(GTK_ENTRY(album_entry) , "ALBUM"); + get_entry_tag(GTK_ENTRY(date_entry) , "DATE"); + get_entry_tag(GTK_ENTRY(tracknum_entry) , "TRACKNUMBER"); + get_entry_tag(GTK_ENTRY(comment_entry) , "DESCRIPTION"); + get_entry_tag(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), "GENRE"); + + FLAC_plugin__tags_set(current_filename, tags_); + gtk_widget_destroy(window); +} + +static void remove_tag(GtkWidget * w, gpointer data) +{ + (void)w; + (void)data; + + FLAC_plugin__tags_delete_tag(tags_, "TITLE"); + FLAC_plugin__tags_delete_tag(tags_, "ARTIST"); + FLAC_plugin__tags_delete_tag(tags_, "ALBUM"); + FLAC_plugin__tags_delete_tag(tags_, "DATE"); + FLAC_plugin__tags_delete_tag(tags_, "TRACKNUMBER"); + FLAC_plugin__tags_delete_tag(tags_, "DESCRIPTION"); + FLAC_plugin__tags_delete_tag(tags_, "GENRE"); + + FLAC_plugin__tags_set(current_filename, tags_); + gtk_widget_destroy(window); +} + +static void show_file_info(void) +{ + FLAC__StreamMetadata streaminfo; + struct stat _stat; + + gtk_label_set_text(GTK_LABEL(flac_samplerate), ""); + gtk_label_set_text(GTK_LABEL(flac_channels), ""); + gtk_label_set_text(GTK_LABEL(flac_bits_per_sample), ""); + gtk_label_set_text(GTK_LABEL(flac_blocksize), ""); + gtk_label_set_text(GTK_LABEL(flac_filesize), ""); + gtk_label_set_text(GTK_LABEL(flac_samples), ""); + gtk_label_set_text(GTK_LABEL(flac_bitrate), ""); + + if(!FLAC__metadata_get_streaminfo(current_filename, &streaminfo)) { + return; + } + + label_set_text(flac_samplerate, _("Samplerate: %d Hz"), streaminfo.data.stream_info.sample_rate); + label_set_text(flac_channels, _("Channels: %d"), streaminfo.data.stream_info.channels); + label_set_text(flac_bits_per_sample, _("Bits/Sample: %d"), streaminfo.data.stream_info.bits_per_sample); + if(streaminfo.data.stream_info.min_blocksize == streaminfo.data.stream_info.max_blocksize) + label_set_text(flac_blocksize, _("Blocksize: %d"), streaminfo.data.stream_info.min_blocksize); + else + label_set_text(flac_blocksize, _("Blocksize: variable\n min/max: %d/%d"), streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize); + + if (streaminfo.data.stream_info.total_samples) + label_set_text(flac_samples, _("Samples: %llu\nLength: %d:%.2d"), + streaminfo.data.stream_info.total_samples, + (int)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate / 60), + (int)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate % 60)); + + if(!stat(current_filename, &_stat) && S_ISREG(_stat.st_mode)) { + label_set_text(flac_filesize, _("Filesize: %ld B"), _stat.st_size); + if (streaminfo.data.stream_info.total_samples) + label_set_text(flac_bitrate, _("Avg. bitrate: %.1f kb/s\nCompression ratio: %.1f%%"), + 8.0 * (float)(_stat.st_size) / (1000.0 * (float)streaminfo.data.stream_info.total_samples / (float)streaminfo.data.stream_info.sample_rate), + 100.0 * (float)_stat.st_size / (float)(streaminfo.data.stream_info.bits_per_sample / 8 * streaminfo.data.stream_info.channels * streaminfo.data.stream_info.total_samples)); + } +} + +void FLAC_XMMS__file_info_box(char *filename) +{ + unsigned i; + gchar *title; + + if (!window) + { + GtkWidget *vbox, *hbox, *left_vbox, *table; + GtkWidget *flac_frame, *flac_box; + GtkWidget *label, *filename_hbox; + GtkWidget *bbox, *save, *remove, *cancel; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &window); + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(window), vbox); + + filename_hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox), filename_hbox, FALSE, TRUE, 0); + + label = gtk_label_new(_("Filename:")); + gtk_box_pack_start(GTK_BOX(filename_hbox), label, FALSE, TRUE, 0); + filename_entry = gtk_entry_new(); + gtk_editable_set_editable(GTK_EDITABLE(filename_entry), FALSE); + gtk_box_pack_start(GTK_BOX(filename_hbox), filename_entry, TRUE, TRUE, 0); + + hbox = gtk_hbox_new(FALSE, 10); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); + + left_vbox = gtk_vbox_new(FALSE, 10); + gtk_box_pack_start(GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0); + + tag_frame = gtk_frame_new(_("Tag:")); + gtk_box_pack_start(GTK_BOX(left_vbox), tag_frame, FALSE, FALSE, 0); + + table = gtk_table_new(5, 5, FALSE); + gtk_container_set_border_width(GTK_CONTAINER(table), 5); + gtk_container_add(GTK_CONTAINER(tag_frame), table); + + label = gtk_label_new(_("Title:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 5, 5); + + title_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), title_entry, 1, 4, 0, 1, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new(_("Artist:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 5, 5); + + artist_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), artist_entry, 1, 4, 1, 2, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new(_("Album:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, GTK_FILL, GTK_FILL, 5, 5); + + album_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), album_entry, 1, 4, 2, 3, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new(_("Comment:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, GTK_FILL, GTK_FILL, 5, 5); + + comment_entry = gtk_entry_new(); + gtk_table_attach(GTK_TABLE(table), comment_entry, 1, 4, 3, 4, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new(_("Date:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 4, 5, GTK_FILL, GTK_FILL, 5, 5); + + date_entry = gtk_entry_new(); + gtk_widget_set_usize(date_entry, 40, -1); + gtk_table_attach(GTK_TABLE(table), date_entry, 1, 2, 4, 5, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new(_("Track number:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 2, 3, 4, 5, GTK_FILL, GTK_FILL, 5, 5); + + tracknum_entry = gtk_entry_new(); + gtk_widget_set_usize(tracknum_entry, 40, -1); + gtk_table_attach(GTK_TABLE(table), tracknum_entry, 3, 4, 4, 5, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + label = gtk_label_new(_("Genre:")); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_table_attach(GTK_TABLE(table), label, 0, 1, 5, 6, GTK_FILL, GTK_FILL, 5, 5); + + genre_combo = gtk_combo_new(); + gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(genre_combo)->entry), TRUE); + + if (!genre_list) + { + for (i = 0; i < sizeof(vorbis_genres) / sizeof(*vorbis_genres) ; i++) + genre_list = g_list_prepend(genre_list, (char *)vorbis_genres[i]); + genre_list = g_list_prepend(genre_list, ""); + genre_list = g_list_sort(genre_list, (GCompareFunc)g_strcasecmp); + } + gtk_combo_set_popdown_strings(GTK_COMBO(genre_combo), genre_list); + + gtk_table_attach(GTK_TABLE(table), genre_combo, 1, 4, 5, 6, GTK_FILL | GTK_EXPAND | GTK_SHRINK, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 5); + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(left_vbox), bbox, FALSE, FALSE, 0); + + save = gtk_button_new_with_label(_("Save")); + gtk_signal_connect(GTK_OBJECT(save), "clicked", GTK_SIGNAL_FUNC(save_tag), NULL); + GTK_WIDGET_SET_FLAGS(save, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), save, TRUE, TRUE, 0); + gtk_widget_grab_default(save); + + remove= gtk_button_new_with_label(_("Remove Tag")); + gtk_signal_connect(GTK_OBJECT(remove), "clicked", GTK_SIGNAL_FUNC(remove_tag), NULL); + GTK_WIDGET_SET_FLAGS(remove, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), remove, TRUE, TRUE, 0); + + cancel = gtk_button_new_with_label(_("Cancel")); + gtk_signal_connect_object(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(window)); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + flac_frame = gtk_frame_new(_("FLAC Info:")); + gtk_box_pack_start(GTK_BOX(hbox), flac_frame, FALSE, FALSE, 0); + + flac_box = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(flac_frame), flac_box); + gtk_container_set_border_width(GTK_CONTAINER(flac_box), 10); + gtk_box_set_spacing(GTK_BOX(flac_box), 0); + + flac_samplerate = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_samplerate), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_samplerate), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_samplerate, FALSE, FALSE, 0); + + flac_channels = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_channels), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_channels), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_channels, FALSE, FALSE, 0); + + flac_bits_per_sample = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_bits_per_sample), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_bits_per_sample), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_bits_per_sample, FALSE, FALSE, 0); + + flac_blocksize = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_blocksize), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_blocksize), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_blocksize, FALSE, FALSE, 0); + + flac_filesize = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_filesize), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_filesize), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_filesize, FALSE, FALSE, 0); + + flac_samples = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_samples), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_samples), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_samples, FALSE, FALSE, 0); + + flac_bitrate = gtk_label_new(""); + gtk_misc_set_alignment(GTK_MISC(flac_bitrate), 0, 0); + gtk_label_set_justify(GTK_LABEL(flac_bitrate), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(flac_box), flac_bitrate, FALSE, FALSE, 0); + + gtk_widget_show_all(window); + } + + if(current_filename) + g_free(current_filename); + if(!(current_filename = g_strdup(filename))) + return; + + title = g_strdup_printf(_("File Info - %s"), g_basename(filename)); + gtk_window_set_title(GTK_WINDOW(window), title); + g_free(title); + + gtk_entry_set_text(GTK_ENTRY(filename_entry), filename); + gtk_editable_set_position(GTK_EDITABLE(filename_entry), -1); + + if(tags_) + FLAC_plugin__tags_destroy(&tags_); + + FLAC_plugin__tags_get(current_filename, &tags_); + + show_tag(); + show_file_info(); + + gtk_widget_set_sensitive(tag_frame, TRUE); +} --- src/Makefile.am-dist 2005-01-25 05:14:05.000000000 +0100 +++ src/Makefile.am 2005-05-25 15:36:41.000000000 +0200 @@ -34,6 +34,7 @@ monkeys_audio_utilities \ plugin_common \ $(XMMS_DIRS) \ + plugin_bmp \ plugin_winamp2 \ test_grabbag \ test_libFLAC \ --- configure.in-dist 2005-02-03 06:03:25.000000000 +0100 +++ configure.in 2005-05-25 15:36:41.000000000 +0200 @@ -160,6 +160,10 @@ AM_PATH_XMMS(0.9.5.1, , AC_MSG_WARN([*** XMMS >= 0.9.5.1 not installed - xmms support will not be built])) AM_CONDITIONAL(FLaC__HAS_XMMS, test x$XMMS_INPUT_PLUGIN_DIR != x) +PKG_CHECK_MODULES(BMP, bmp) +BMP_INPUT_PLUGIN_DIR=`pkg-config --variable=input_plugin_dir bmp` +AC_SUBST(BMP_INPUT_PLUGIN_DIR) + dnl check for i18n(internationalization); these are from libiconv/gettext AM_ICONV AM_LANGINFO_CODESET @@ -259,6 +263,7 @@ src/plugin_winamp2/include/Makefile \ src/plugin_winamp2/include/winamp2/Makefile \ src/plugin_xmms/Makefile \ + src/plugin_bmp/Makefile \ src/share/Makefile \ src/share/getopt/Makefile \ src/share/grabbag/Makefile \