Mercurial > audlegacy-plugins
changeset 3059:2e241e90494a
Import work in progress xmms-crossfade rewrite.
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Fri, 24 Apr 2009 05:57:35 -0500 |
parents | 2e649bf16ebc |
children | ff7130782918 |
files | src/crossfade/Makefile src/crossfade/callbacks.c src/crossfade/callbacks.h src/crossfade/cfgutil.c src/crossfade/cfgutil.h src/crossfade/configure.c src/crossfade/configure.h src/crossfade/convert.c src/crossfade/convert.h src/crossfade/crossfade.c src/crossfade/crossfade.h src/crossfade/debug.c src/crossfade/debug.h src/crossfade/format.c src/crossfade/format.h src/crossfade/interface-2.0.c src/crossfade/interface-2.0.h src/crossfade/monitor.c src/crossfade/monitor.h src/crossfade/player.c src/crossfade/player.h src/crossfade/support-2.0.c src/crossfade/support-2.0.h src/crossfade/timing.c src/crossfade/timing.h |
diffstat | 25 files changed, 10395 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/Makefile Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,23 @@ +PLUGIN = crossfade${PLUGIN_SUFFIX} + +SRCS = callbacks.c \ + cfgutil.c \ + configure.c \ + convert.c \ + crossfade.c \ + debug.c \ + format.c \ + interface-2.0.c \ + monitor.c \ + player.c \ + support-2.0.c \ + timing.c \ + +include ../../buildsys.mk +include ../../extra.mk + +plugindir := ${plugindir}/${OUTPUT_PLUGIN_DIR} + +CFLAGS += ${PLUGIN_CFLAGS} +CPPFLAGS += ${PLUGIN_CPPFLAGS} ${MOWGLI_CFLAGS} ${GTK_CFLAGS} ${GLIB_CFLAGS} ${ALSA_CFLAGS} -I../.. +LIBS += ${GTK_LIBS} ${GLIB_LIBS} ${ALSA_LIBS}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/callbacks.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,38 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "crossfade.h" + +#include "callbacks.h" +#include "interface-2.0.h" +#include "support-2.0.h" + +void +on_help_close_button_clicked(GtkButton * button, gpointer user_data) +{ + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/callbacks.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,99 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef _CALLBACKS_H_ +#define _CALLBACKS_H_ + +#include <gtk/gtk.h> + +/* configure.c*/ +void on_config_ok_clicked(GtkButton * button, gpointer user_data); +void on_config_apply_clicked(GtkButton * button, gpointer user_data); +void on_output_oss_radio_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_output_plugin_radio_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_output_none_radio_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_config_adevice_alt_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_config_mdevice_alt_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_osshwb_maxbuf_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_config_crossfade_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_config_mixopt_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_output_plugin_configure_button_clicked(GtkButton * button, gpointer user_data); +void on_output_plugin_about_button_clicked(GtkButton * button, gpointer user_data); +void on_op_throttle_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_op_maxblock_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_op_maxblock_spin_changed(GtkEditable * editable, gpointer user_data); +void on_op_forcereopen_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_ep_configure_button_clicked(GtkButton * button, gpointer user_data); +void on_ep_about_button_clicked(GtkButton * button, gpointer user_data); +void on_ep_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_volnorm_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xftfp_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xftfp_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_xftffi_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xftffi_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_xftffi_volume_spin_changed(GtkEditable * editable, gpointer user_data); +void on_pause_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_simple_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_xf_buffer_spin_changed(GtkEditable * editable, gpointer user_data); +void on_xf_autobuf_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_fadeout_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xfofs_none_radiobutton_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xfofs_none_radiobutton_clicked(GtkButton * button, gpointer user_data); +void on_xfofs_lockout_radiobutton_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xfofs_lockout_radiobutton_clicked(GtkButton * button, gpointer user_data); +void on_xfofs_lockin_radiobutton_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xfofs_lockin_radiobutton_clicked(GtkButton * button, gpointer user_data); +void on_xfofs_custom_radiobutton_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_xfofs_custom_radiobutton_clicked(GtkButton * button, gpointer user_data); +void on_xfofs_custom_spin_changed(GtkEditable * editable, gpointer user_data); +void on_fadein_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_fadein_lock_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_lgap_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_tgap_enable_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_tgap_lock_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_fadeout_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_fadeout_volume_spin_changed(GtkEditable * editable, gpointer user_data); +void on_fadein_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_fadein_volume_spin_changed(GtkEditable * editable, gpointer user_data); +void on_lgap_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_lgap_level_spin_changed(GtkEditable * editable, gpointer user_data); +void on_tgap_length_spin_changed(GtkEditable * editable, gpointer user_data); +void on_tgap_level_spin_changed(GtkEditable * editable, gpointer user_data); +void on_gapkiller_default_button_clicked(GtkButton * button, gpointer user_data); +void on_moth_songchange_spin_changed(GtkEditable * editable, gpointer user_data); +void on_moth_opmaxused_check_toggled(GtkToggleButton * togglebutton, gpointer user_data); +void on_misc_default_button_clicked(GtkButton * button, gpointer user_data); +void on_presets_list_click_column(GtkCList * clist, gint column, gpointer user_data); + +/* monitor.c */ +gboolean on_monitor_display_drawingarea_expose_event(GtkWidget * widget, GdkEventExpose * event, gpointer user_data); +gboolean on_monitor_win_delete_event(GtkWidget * widget, GdkEvent * event, gpointer user_data); + +/* help.c (not yet implemented) */ +void on_help_close_button_clicked(GtkButton * button, gpointer user_data); + +#endif /* _CALLBACKS_H_ */ + +void +on_monitor_seekeof_button_clicked (GtkButton *button, + gpointer user_data);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/cfgutil.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,752 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#undef PRESET_SUPPORT + +#include "crossfade.h" +#include "configure.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +# define ConfigFile ConfigDb +# define xmms_cfg_read_int aud_cfg_db_get_int +# define xmms_cfg_read_string aud_cfg_db_get_string +# define xmms_cfg_read_boolean aud_cfg_db_get_bool +# define xmms_cfg_write_int aud_cfg_db_set_int +# define xmms_cfg_write_string aud_cfg_db_set_string +# define xmms_cfg_write_boolean aud_cfg_db_set_bool +# define xmms_cfg_remove_key aud_cfg_db_unset_key +# define xmms_cfg_open_default_file aud_cfg_db_open +# define xmms_cfg_write_default_file aud_cfg_db_close +void xmms_cfg_dummy(ConfigDb *dummy) { } +# define xmms_cfg_free xmms_cfg_dummy + +/* init with DEFAULT_CFG to make sure all string pointers are set to NULL */ +config_t _xfg = CONFIG_DEFAULT; /* also used in configure.c */ +static config_t *xfg = &_xfg; + +/*****************************************************************************/ + +static void +update_plugin_config(gchar **config_string, gchar *name, plugin_config_t *pc, gboolean save); + +static void +g_free_f(gpointer data, gpointer user_data) +{ + g_free(data); +} + +/*****************************************************************************/ + +static gchar *strip(gchar *s) +{ + gchar *p; + if (!s) + return NULL; + + for (; *s == ' '; s++); + if (!*s) + return s; + + for (p = s + strlen(s) - 1; *p == ' '; p--); + *++p = 0; + return s; +} + +static void +update_plugin_config(gchar **config_string, gchar *name, plugin_config_t *pc, gboolean save) +{ + plugin_config_t default_pc = DEFAULT_OP_CONFIG; + + gchar *buffer = NULL; + gchar out[1024]; + + gboolean plugin_found = FALSE; + gchar *plugin, *next_plugin; + gchar *args; + + if (pc && !save) + *pc = default_pc; + + if (!config_string || !*config_string || !name || !pc) + { + DEBUG(("[crossfade] update_plugin_config: missing arg!\n")); + return; + } + + buffer = g_strdup(*config_string); + out[0] = 0; + + for (plugin = buffer; plugin; plugin = next_plugin) + { + if ((next_plugin = strchr(plugin, ';'))) + *next_plugin++ = 0; + + if ((args = strchr(plugin, '='))) + *args++ = 0; + + plugin = strip(plugin); + if (!*plugin || !args || !*args) + continue; + + if (save) + { + if (strcmp(plugin, name) == 0) + continue; + + if (*out) + strcat(out, "; "); + + strcat(out, plugin); + strcat(out, "="); + strcat(out, args); + continue; + } + else if (strcmp(plugin, name)) + continue; + + args = strip(args); + sscanf(args, "%d,%d,%d,%d", &pc->throttle_enable, &pc->max_write_enable, &pc->max_write_len, &pc->force_reopen); + pc->max_write_len &= -4; + plugin_found = TRUE; + } + + if (save) + { + /* only save if settings differ from defaults */ + if ((pc->throttle_enable != default_pc.throttle_enable) + || (pc->max_write_enable != default_pc.max_write_enable) + || (pc->max_write_len != default_pc.max_write_len) + || (pc->force_reopen != default_pc.force_reopen)) + { + if (*out) + strcat(out, "; "); + + sprintf(out + strlen(out), "%s=%d,%d,%d,%d", name, + pc->throttle_enable ? 1 : 0, pc->max_write_enable ? 1 : 0, pc->max_write_len, pc->force_reopen); + } + if (*config_string) + g_free(*config_string); + + *config_string = g_strdup(out); + } + + g_free(buffer); +} + +/*****************************************************************************/ + +#ifdef PRESET_SUPPORT +static void +scan_presets(gchar *filename) +{ + struct stat stats; + FILE *fh; + gchar *data, **lines, *tmp, *name; + int i; + + if (lstat(filename, &stats)) + { + DEBUG(("[crossfade] scan_presets: \"%s\":\n", filename)); + PERROR("[crossfade] scan_presets: lstat"); + return; + } + if (stats.st_size <= 0) + return; + + if (!(data = g_malloc(stats.st_size + 1))) + { + DEBUG(("[crossfade] scan_presets: g_malloc(%ld) failed!\n", stats.st_size)); + return; + } + + if (!(fh = fopen(filename, "r"))) + { + PERROR("[crossfade] scan_presets: fopen"); + g_free(data); + return; + } + + if (fread(data, stats.st_size, 1, fh) != 1) + { + DEBUG(("[crossfade] scan_presets: fread() failed!\n")); + g_free(data); + fclose(fh); + return; + } + fclose(fh); + data[stats.st_size] = 0; + + lines = g_strsplit(data, "\n", 0); + g_free(data); + + if (!lines) + { + DEBUG(("[crossfade] scan_presets: g_strsplit() failed!\n")); + return; + } + + g_list_foreach(config->presets, g_free_f, NULL); + g_list_free(config->presets); + config->presets = NULL; + + for (i = 0; lines[i]; i++) + { + if (lines[i][0] == '[') + { + if ((tmp = strchr(lines[i], ']'))) + { + *tmp = 0; + if ((name = g_strdup(lines[i] + 1))) + config->presets = g_list_append(config->presets, name); + } + } + } + + g_strfreev(lines); +} +#endif + +static void +read_fade_config(ConfigFile *cfgfile, gchar *section, gchar *key, fade_config_t *fc) +{ + gchar *s = NULL; + gint n; + + if (!cfgfile || !section || !key || !fc) + return; + + xmms_cfg_read_string(cfgfile, section, key, &s); + if (!s) + return; + + n = sscanf(s, + "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &fc->type, + &fc->pause_len_ms, + &fc->simple_len_ms, + &fc->out_enable, + &fc->out_len_ms, + &fc->out_volume, + &fc->ofs_type, + &fc->ofs_type_wanted, + &fc->ofs_custom_ms, + &fc->in_locked, + &fc->in_enable, + &fc->in_len_ms, + &fc->in_volume, + &fc->flush_pause_enable, + &fc->flush_pause_len_ms, &fc->flush_in_enable, &fc->flush_in_len_ms, &fc->flush_in_volume); + + g_free(s); +} + +static void +write_fade_config(ConfigFile *cfgfile, gchar *section, gchar *key, fade_config_t *fc) +{ + gchar *s; + + if (!cfgfile || !section || !key || !fc) + return; + + s = g_strdup_printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + fc->type, + fc->pause_len_ms, + fc->simple_len_ms, + fc->out_enable, + fc->out_len_ms, + fc->out_volume, + fc->ofs_type, + fc->ofs_type_wanted, + fc->ofs_custom_ms, + fc->in_locked, + fc->in_enable, + fc->in_len_ms, + fc->in_volume, + fc->flush_pause_enable, + fc->flush_pause_len_ms, fc->flush_in_enable, fc->flush_in_len_ms, fc->flush_in_volume); + + if (!s) + return; + + xmms_cfg_write_string(cfgfile, section, key, s); + g_free(s); +} + +void +xfade_load_config() +{ +#ifdef PRESET_SUPPORT + gchar *filename; +#endif + gchar *section = "Crossfade"; + ConfigFile *cfgfile; + + if ((cfgfile = xmms_cfg_open_default_file())) + { + /* *INDENT-OFF* */ + /* config items used in v0.1 */ + xmms_cfg_read_int (cfgfile, section, "output_method", &config->output_method); + xmms_cfg_read_int (cfgfile, section, "audio_device", &config->oss_audio_device); + xmms_cfg_read_boolean(cfgfile, section, "use_alt_audio_device", &config->oss_use_alt_audio_device); + xmms_cfg_read_string (cfgfile, section, "alt_audio_device", &config->oss_alt_audio_device); + xmms_cfg_read_int (cfgfile, section, "mixer_device", &config->oss_mixer_device); + xmms_cfg_read_string (cfgfile, section, "output_plugin", &config->op_name); + xmms_cfg_read_string (cfgfile, section, "op_config_string", &config->op_config_string); + xmms_cfg_read_int (cfgfile, section, "buffer_size", &config->mix_size_ms); + xmms_cfg_read_int (cfgfile, section, "sync_size", &config->sync_size_ms); + xmms_cfg_read_int (cfgfile, section, "preload_size", &config->preload_size_ms); + xmms_cfg_read_int (cfgfile, section, "songchange_timeout", &config->songchange_timeout); + xmms_cfg_read_boolean(cfgfile, section, "enable_mixer", &config->enable_mixer); + xmms_cfg_read_boolean(cfgfile, section, "mixer_reverse", &config->mixer_reverse); + xmms_cfg_read_boolean(cfgfile, section, "enable_debug", &config->enable_debug); + xmms_cfg_read_boolean(cfgfile, section, "enable_monitor", &config->enable_monitor); + + /* config items introduced by v0.2 */ + xmms_cfg_read_int (cfgfile, section, "oss_buffer_size", &config->oss_buffer_size_ms); + xmms_cfg_read_int (cfgfile, section, "oss_preload_size", &config->oss_preload_size_ms); + xmms_cfg_read_boolean(cfgfile, section, "oss_mixer_use_master", &config->oss_mixer_use_master); + xmms_cfg_read_boolean(cfgfile, section, "gap_lead_enable", &config->gap_lead_enable); + xmms_cfg_read_int (cfgfile, section, "gap_lead_len_ms", &config->gap_lead_len_ms); + xmms_cfg_read_int (cfgfile, section, "gap_lead_level", &config->gap_lead_level); + xmms_cfg_read_boolean(cfgfile, section, "gap_trail_enable", &config->gap_trail_enable); + xmms_cfg_read_int (cfgfile, section, "gap_trail_len_ms", &config->gap_trail_len_ms); + xmms_cfg_read_int (cfgfile, section, "gap_trail_level", &config->gap_trail_level); + xmms_cfg_read_int (cfgfile, section, "gap_trail_locked", &config->gap_trail_locked); + + /* config items introduced by v0.2.1 */ + xmms_cfg_read_boolean(cfgfile, section, "buffer_size_auto", &config->mix_size_auto); + + /* config items introduced by v0.2.3 */ + xmms_cfg_read_boolean(cfgfile, section, "album_detection", &config->album_detection); + + /* config items introduced by v0.2.4 */ + xmms_cfg_read_boolean(cfgfile, section, "http_workaround", &config->enable_http_workaround); + xmms_cfg_read_boolean(cfgfile, section, "enable_op_max_used", &config->enable_op_max_used); + xmms_cfg_read_int (cfgfile, section, "op_max_used_ms", &config->op_max_used_ms); + + /* config items introduced by v0.2.6 */ + xmms_cfg_read_string (cfgfile, section, "effect_plugin", &config->ep_name); + xmms_cfg_read_boolean(cfgfile, section, "effect_enable", &config->ep_enable); + xmms_cfg_read_int (cfgfile, section, "output_rate", &config->output_rate); + + /* config items introduced by v0.2.7 */ + xmms_cfg_read_boolean(cfgfile, section, "oss_maxbuf_enable", &config->oss_maxbuf_enable); + + /* config items introduced by v0.3.0 */ + xmms_cfg_read_boolean(cfgfile, section, "use_alt_mixer_device", &config->oss_use_alt_mixer_device); + xmms_cfg_read_int (cfgfile, section, "oss_fragments", &config->oss_fragments); + xmms_cfg_read_int (cfgfile, section, "oss_fragment_size", &config->oss_fragment_size); + xmms_cfg_read_boolean(cfgfile, section, "volnorm_enable", &config->volnorm_enable); + xmms_cfg_read_boolean(cfgfile, section, "volnorm_use_qa", &config->volnorm_use_qa); + xmms_cfg_read_int (cfgfile, section, "volnorm_target", &config->volnorm_target); + xmms_cfg_read_boolean(cfgfile, section, "output_keep_opened", &config->output_keep_opened); + xmms_cfg_read_boolean(cfgfile, section, "mixer_software", &config->mixer_software); + xmms_cfg_read_int (cfgfile, section, "mixer_vol_left", &config->mixer_vol_left); + xmms_cfg_read_int (cfgfile, section, "mixer_vol_right", &config->mixer_vol_right); + + /* config items introduced by v0.3.2 */ + xmms_cfg_read_boolean(cfgfile, section, "no_xfade_if_same_file",&config->no_xfade_if_same_file); + + /* config items introduced by v0.3.3 */ + xmms_cfg_read_string (cfgfile, section, "alt_mixer_device", &config->oss_alt_mixer_device); + xmms_cfg_read_boolean(cfgfile, section, "gap_crossing", &config->gap_crossing); + + /* config items introduced by v0.3.6 */ + xmms_cfg_read_int (cfgfile, section, "output_quality", &config->output_quality); + + /* fade configs */ + read_fade_config(cfgfile, section, "fc_xfade", &config->fc[FADE_CONFIG_XFADE]); + read_fade_config(cfgfile, section, "fc_manual", &config->fc[FADE_CONFIG_MANUAL]); + read_fade_config(cfgfile, section, "fc_album", &config->fc[FADE_CONFIG_ALBUM]); + read_fade_config(cfgfile, section, "fc_start", &config->fc[FADE_CONFIG_START]); + read_fade_config(cfgfile, section, "fc_stop", &config->fc[FADE_CONFIG_STOP]); + read_fade_config(cfgfile, section, "fc_eop", &config->fc[FADE_CONFIG_EOP]); + read_fade_config(cfgfile, section, "fc_seek", &config->fc[FADE_CONFIG_SEEK]); + read_fade_config(cfgfile, section, "fc_pause", &config->fc[FADE_CONFIG_PAUSE]); + + xmms_cfg_free(cfgfile); + DEBUG(("[crossfade] load_config: configuration loaded\n")); + } + else + DEBUG(("[crossfade] load_config: error loading config, using defaults\n")); + +#ifdef PRESET_SUPPORT +#ifdef COMPILE_FOR_AUDACIOUS + filename = g_strconcat(g_get_home_dir(), "/.audacious/crossfade-presets", NULL); +#else + filename = g_strconcat(g_get_home_dir(), "/.xmms/crossfade-presets", NULL); +#endif + scan_presets(filename); + g_free(filename); +#endif +} + +void +xfade_save_config() +{ + gchar *section = "Crossfade"; + ConfigFile *cfgfile; + + if ((cfgfile = xmms_cfg_open_default_file())) + { + /* obsolete config items */ + xmms_cfg_remove_key(cfgfile, section, "underrun_pct"); + xmms_cfg_remove_key(cfgfile, section, "enable_crossfade"); + xmms_cfg_remove_key(cfgfile, section, "enable_gapkiller"); + xmms_cfg_remove_key(cfgfile, section, "mixer_use_master"); + xmms_cfg_remove_key(cfgfile, section, "late_effect"); + xmms_cfg_remove_key(cfgfile, section, "gap_lead_length"); + + /* config items used in v0.1 */ + xmms_cfg_write_int (cfgfile, section, "output_method", config->output_method); + xmms_cfg_write_int (cfgfile, section, "audio_device", config->oss_audio_device); + xmms_cfg_write_boolean(cfgfile, section, "use_alt_audio_device", config->oss_use_alt_audio_device); + xmms_cfg_write_string (cfgfile, section, "alt_audio_device", config->oss_alt_audio_device ? config->oss_alt_audio_device : DEFAULT_OSS_ALT_AUDIO_DEVICE); + xmms_cfg_write_int (cfgfile, section, "mixer_device", config->oss_mixer_device); + xmms_cfg_write_string (cfgfile, section, "output_plugin", config->op_name ? config->op_name : DEFAULT_OP_NAME); + xmms_cfg_write_string (cfgfile, section, "op_config_string", config->op_config_string ? config->op_config_string : DEFAULT_OP_CONFIG_STRING); + xmms_cfg_write_int (cfgfile, section, "buffer_size", config->mix_size_ms); + xmms_cfg_write_int (cfgfile, section, "sync_size", config->sync_size_ms); + xmms_cfg_write_int (cfgfile, section, "preload_size", config->preload_size_ms); + xmms_cfg_write_int (cfgfile, section, "songchange_timeout", config->songchange_timeout); + xmms_cfg_write_boolean(cfgfile, section, "enable_mixer", config->enable_mixer); + xmms_cfg_write_boolean(cfgfile, section, "mixer_reverse", config->mixer_reverse); + xmms_cfg_write_boolean(cfgfile, section, "enable_debug", config->enable_debug); + xmms_cfg_write_boolean(cfgfile, section, "enable_monitor", config->enable_monitor); + + /* config items introduced by v0.2 */ + xmms_cfg_write_int (cfgfile, section, "oss_buffer_size", config->oss_buffer_size_ms); + xmms_cfg_write_int (cfgfile, section, "oss_preload_size", config->oss_preload_size_ms); + xmms_cfg_write_boolean(cfgfile, section, "oss_mixer_use_master", config->oss_mixer_use_master); + xmms_cfg_write_boolean(cfgfile, section, "gap_lead_enable", config->gap_lead_enable); + xmms_cfg_write_int (cfgfile, section, "gap_lead_len_ms", config->gap_lead_len_ms); + xmms_cfg_write_int (cfgfile, section, "gap_lead_level", config->gap_lead_level); + xmms_cfg_write_boolean(cfgfile, section, "gap_trail_enable", config->gap_trail_enable); + xmms_cfg_write_int (cfgfile, section, "gap_trail_len_ms", config->gap_trail_len_ms); + xmms_cfg_write_int (cfgfile, section, "gap_trail_level", config->gap_trail_level); + xmms_cfg_write_int (cfgfile, section, "gap_trail_locked", config->gap_trail_locked); + + /* config items introduced by v0.2.1 */ + xmms_cfg_write_boolean(cfgfile, section, "buffer_size_auto", config->mix_size_auto); + + /* config items introduced by v0.2.3 */ + xmms_cfg_write_boolean(cfgfile, section, "album_detection", config->album_detection); + + /* config items introduced by v0.2.4 */ + xmms_cfg_write_boolean(cfgfile, section, "http_workaround", config->enable_http_workaround); + xmms_cfg_write_boolean(cfgfile, section, "enable_op_max_used", config->enable_op_max_used); + xmms_cfg_write_int (cfgfile, section, "op_max_used_ms", config->op_max_used_ms); + + /* config items introduced by v0.2.6 */ + xmms_cfg_write_string (cfgfile, section, "effect_plugin", config->ep_name ? config->ep_name : DEFAULT_EP_NAME); + xmms_cfg_write_boolean(cfgfile, section, "effect_enable", config->ep_enable); + xmms_cfg_write_int (cfgfile, section, "output_rate", config->output_rate); + + /* config items introduced by v0.2.7 */ + xmms_cfg_write_boolean(cfgfile, section, "oss_maxbuf_enable", config->oss_maxbuf_enable); + + /* config items introduced by v0.3.0 */ + xmms_cfg_write_boolean(cfgfile, section, "use_alt_mixer_device", config->oss_use_alt_mixer_device); + xmms_cfg_write_int (cfgfile, section, "oss_fragments", config->oss_fragments); + xmms_cfg_write_int (cfgfile, section, "oss_fragment_size", config->oss_fragment_size); +#ifdef VOLUME_NORMALIZER + xmms_cfg_write_boolean(cfgfile, section, "volnorm_enable", config->volnorm_enable); + xmms_cfg_write_boolean(cfgfile, section, "volnorm_use_qa", config->volnorm_use_qa); + xmms_cfg_write_int (cfgfile, section, "volnorm_target", config->volnorm_target); +#endif + xmms_cfg_write_boolean(cfgfile, section, "output_keep_opened", config->output_keep_opened); + xmms_cfg_write_boolean(cfgfile, section, "mixer_software", config->mixer_software); + xmms_cfg_write_int (cfgfile, section, "mixer_vol_left", config->mixer_vol_left); + xmms_cfg_write_int (cfgfile, section, "mixer_vol_right", config->mixer_vol_right); + + /* config items introduced by v0.3.2 */ + xmms_cfg_write_boolean(cfgfile, section, "no_xfade_if_same_file",config->no_xfade_if_same_file); + + /* config items introduced by v0.3.2 */ + xmms_cfg_write_string (cfgfile, section, "alt_mixer_device", config->oss_alt_mixer_device ? config->oss_alt_mixer_device : DEFAULT_OSS_ALT_MIXER_DEVICE); + xmms_cfg_write_boolean(cfgfile, section, "gap_crossing", config->gap_crossing); + + /* config items introduced by v0.3.6 */ + xmms_cfg_write_int(cfgfile, section, "output_quality", config->output_quality); + + /* fade configs */ + write_fade_config(cfgfile, section, "fc_xfade", &config->fc[FADE_CONFIG_XFADE]); + write_fade_config(cfgfile, section, "fc_manual", &config->fc[FADE_CONFIG_MANUAL]); + write_fade_config(cfgfile, section, "fc_album", &config->fc[FADE_CONFIG_ALBUM]); + write_fade_config(cfgfile, section, "fc_start", &config->fc[FADE_CONFIG_START]); + write_fade_config(cfgfile, section, "fc_stop", &config->fc[FADE_CONFIG_STOP]); + write_fade_config(cfgfile, section, "fc_eop", &config->fc[FADE_CONFIG_EOP]); + write_fade_config(cfgfile, section, "fc_seek", &config->fc[FADE_CONFIG_SEEK]); + write_fade_config(cfgfile, section, "fc_pause", &config->fc[FADE_CONFIG_PAUSE]); + /* *INDENT-ON* */ + + xmms_cfg_write_default_file(cfgfile); + xmms_cfg_free(cfgfile); + DEBUG(("[crossfade] save_config: configuration saved\n")); + } + else + DEBUG(("[crossfade] save_config: error saving configuration!\n")); +} + +#define SAFE_FREE(x) if(x) { g_free(x); x = NULL; } +void +xfade_free_config() +{ + SAFE_FREE(xfg->oss_alt_audio_device); + SAFE_FREE(xfg->oss_alt_mixer_device); + SAFE_FREE(xfg->op_config_string); + SAFE_FREE(xfg->op_name); + + g_list_foreach(config->presets, g_free_f, NULL); + g_list_free(config->presets); + config->presets = NULL; +} + +void +xfade_load_plugin_config(gchar *config_string, gchar *plugin_name, plugin_config_t *plugin_config) +{ + update_plugin_config(&config_string, plugin_name, plugin_config, FALSE); +} + +void +xfade_save_plugin_config(gchar **config_string, gchar *plugin_name, plugin_config_t *plugin_config) +{ + update_plugin_config(config_string, plugin_name, plugin_config, TRUE); +} + +/*** helpers *****************************************************************/ + +gint +xfade_cfg_out_skip(fade_config_t *fc) +{ + if (!fc) + return 0; + + switch (fc->config) + { + case FADE_CONFIG_TIMING: + return fc->out_skip_ms; + } + return 0; +} + +gint +xfade_cfg_fadeout_len(fade_config_t *fc) +{ + if (!fc) + return 0; + + switch (fc->type) + { + case FADE_TYPE_SIMPLE_XF: + return fc->simple_len_ms; + + case FADE_TYPE_ADVANCED_XF: + return fc->out_enable ? fc->out_len_ms : 0; + + case FADE_TYPE_FADEOUT: + case FADE_TYPE_PAUSE_ADV: + return fc->out_len_ms; + } + return 0; +} + +gint +xfade_cfg_fadeout_volume(fade_config_t *fc) +{ + gint volume; + if (!fc) + return 0; + + switch (fc->type) + { + case FADE_TYPE_ADVANCED_XF: + case FADE_TYPE_FADEOUT: + volume = fc->out_volume; + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + return volume; + } + return 0; +} + +gint +xfade_cfg_offset(fade_config_t *fc) +{ + if (!fc) + return 0; + + switch (fc->type) + { + case FADE_TYPE_FLUSH: + return fc->flush_pause_enable ? fc->flush_pause_len_ms : 0; + + case FADE_TYPE_PAUSE: + return fc->pause_len_ms; + + case FADE_TYPE_SIMPLE_XF: + return -fc->simple_len_ms; + + case FADE_TYPE_ADVANCED_XF: + switch (fc->ofs_type) + { + case FC_OFFSET_LOCK_OUT: return -fc->out_len_ms; + case FC_OFFSET_LOCK_IN: return -fc->in_len_ms; + case FC_OFFSET_CUSTOM: return fc->ofs_custom_ms; + } + return 0; + + case FADE_TYPE_FADEOUT: + case FADE_TYPE_PAUSE_ADV: + return fc->ofs_custom_ms; + } + return 0; +} + +gint +xfade_cfg_in_skip(fade_config_t *fc) +{ + if (!fc) + return 0; + + switch (fc->config) + { + case FADE_CONFIG_TIMING: + return fc->in_skip_ms; + } + return 0; +} + +gint +xfade_cfg_fadein_len(fade_config_t *fc) +{ + if (!fc) + return 0; + + switch (fc->type) + { + case FADE_TYPE_FLUSH: + return fc->flush_in_enable ? fc->flush_in_len_ms : 0; + + case FADE_TYPE_SIMPLE_XF: + return fc->simple_len_ms; + + case FADE_TYPE_ADVANCED_XF: + return fc->in_locked + ? (fc->out_enable ? fc->out_len_ms : 0) + : (fc->in_enable ? fc->in_len_ms : 0); + + case FADE_TYPE_FADEIN: + case FADE_TYPE_PAUSE_ADV: + return fc->in_len_ms; + } + return 0; +} + +gint +xfade_cfg_fadein_volume(fade_config_t *fc) +{ + gint volume; + if (!fc) + return 0; + + switch (fc->type) + { + case FADE_TYPE_FLUSH: + volume = fc->flush_in_volume; + break; + + case FADE_TYPE_ADVANCED_XF: + volume = fc->in_locked ? fc->out_volume : fc->in_volume; + break; + + case FADE_TYPE_FADEIN: + volume = fc->in_volume; + break; + + default: + volume = 0; + } + + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + return volume; +} + +gboolean +xfade_cfg_gap_trail_enable(config_t *cfg) +{ + return xfg->gap_trail_locked ? xfg->gap_lead_enable : xfg->gap_trail_enable; +} + +gint +xfade_cfg_gap_trail_len(config_t *cfg) +{ + if (!xfade_cfg_gap_trail_enable(cfg)) + return 0; + + return xfg->gap_trail_locked ? xfg->gap_lead_len_ms : xfg->gap_trail_len_ms; +} + +gint +xfade_cfg_gap_trail_level(config_t *cfg) +{ + return xfg->gap_trail_locked ? xfg->gap_lead_level : xfg->gap_trail_level; +} + +gint +xfade_mix_size_ms(config_t *cfg) +{ + if (xfg->mix_size_auto) + { + gint i, min_size = 0; + + for (i = 0; i < MAX_FADE_CONFIGS; i++) + { + gint size = xfade_cfg_fadeout_len(&xfg->fc[i]); + gint offset = xfade_cfg_offset(&xfg->fc[i]); + + if (xfg->fc[i].type == FADE_TYPE_PAUSE_ADV) + size += xfade_cfg_fadein_len(&xfg->fc[i]); + + if (size < -offset) + size = -offset; + + if (size > min_size) + min_size = size; + } + return min_size += xfade_cfg_gap_trail_len(cfg) + xfg->songchange_timeout; + } + else + return xfg->mix_size_ms; +} + +/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/cfgutil.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,53 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef _CFGUTIL_H_ +#define _CFGUTIL_H_ + +#include "crossfade.h" + +/* configuration load/save functions */ +void xfade_load_config(); +void xfade_save_config(); +void xfade_free_config(); + +void xfade_load_plugin_config(gchar *config_string, gchar *plugin_name, plugin_config_t *plugin_config); +void xfade_save_plugin_config(gchar **config_string, gchar *plugin_name, plugin_config_t *plugin_confg); + +/* some helper functions */ +gint xfade_mix_size_ms(config_t * cfg); + +gint xfade_cfg_out_skip (fade_config_t *fc); +gint xfade_cfg_fadeout_len (fade_config_t *fc); +gint xfade_cfg_fadeout_volume(fade_config_t *fc); +gint xfade_cfg_offset (fade_config_t *fc); +gint xfade_cfg_in_skip (fade_config_t *fc); +gint xfade_cfg_fadein_len (fade_config_t *fc); +gint xfade_cfg_fadein_volume (fade_config_t *fc); + +gboolean xfade_cfg_gap_trail_enable(config_t *cfg); +gint xfade_cfg_gap_trail_len (config_t *cfg); +gint xfade_cfg_gap_trail_level (config_t *cfg); + +#endif /* _CFGUTIL_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/configure.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,1779 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "crossfade.h" +#include "configure.h" +#include "cfgutil.h" +#include "monitor.h" + +#include "interface-2.0.h" +#include "support-2.0.h" + +#ifdef HAVE_OSS +# include "oss.h" +#endif + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#ifdef HAVE_OSS +# ifdef HAVE_SYS_SOUNDCARD_H +# include <sys/soundcard.h> +# elif defined(HAVE_MACHINE_SOUNDCARD_H) +# include <machine/soundcard.h> +# endif +#endif + +#ifdef HAVE_LIBSAMPLERATE +# include <samplerate.h> +#endif + + +/* available rates for resampling */ +gint sample_rates[] = +{ +#if MAX_RATE > 48000 + 192000, + 96000, + 88200, + 64000, +#endif + 48000, + 44100, + 32000, + 22050, + 16000, + 11025, + 8000, + 6000, + 0 +}; + + +#define HIDE(name) \ +{ if ((set_wgt = lookup_widget(config_win, name))) \ + gtk_widget_hide(set_wgt); } + +#define SHOW(name) \ +{ if ((set_wgt = lookup_widget(config_win, name))) \ + gtk_widget_show(set_wgt); } + + +#define SETW_SENSITIVE(wgt, sensitive) \ + gtk_widget_set_sensitive(wgt, sensitive) + +#define SETW_TOGGLE(wgt, active) \ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wgt), active) + +#define SETW_SPIN(wgt, value) \ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(wgt), value) + + +#define SET_SENSITIVE(name, sensitive) \ +{ if ((set_wgt = lookup_widget(config_win, name))) \ + gtk_widget_set_sensitive(set_wgt, sensitive); } + +#define SET_TOGGLE(name, active) \ +{ if ((set_wgt = lookup_widget(config_win, name))) \ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_wgt), active); } + +#define SET_SPIN(name, value) \ +{ if ((set_wgt = lookup_widget(config_win, name))) \ + gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_wgt), value); } + +#define SET_PAGE(name, index) \ +{ if ((set_wgt = lookup_widget(config_win, name))) \ + gtk_notebook_set_page(GTK_NOTEBOOK(set_wgt), index); } + +#define SET_HISTORY(name, index) \ +{ if((set_wgt = lookup_widget(config_win, name))) \ + gtk_option_menu_set_history(GTK_OPTION_MENU(set_wgt), index); } + + +#define GET_SENSITIVE(name) \ +((get_wgt = lookup_widget(config_win, name)) \ + && GTK_WIDGET_SENSITIVE(get_wgt)) \ + +#define GET_TOGGLE(name) \ +((get_wgt = lookup_widget(config_win, name)) \ + && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(get_wgt))) + +#define GET_SPIN(name) \ +((get_wgt = lookup_widget(config_win, name)) \ + ? gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(get_wgt)) : 0) + + +static GtkWidget *config_win = NULL; +static GtkWidget *about_win = NULL; +static GtkWidget *set_wgt; +static GtkWidget *get_wgt; + +/* defined in cfgutil.c */ +extern config_t _xfg; +static config_t *xfg = &_xfg; + +/* some helpers to keep track of the GUI's state */ +static gboolean checking = FALSE; +static gint op_index; +static plugin_config_t op_config; +static gint ep_index; + +/* from crossfade.c */ +extern MUTEX buffer_mutex; + +/*** internal helpers ********************************************************/ + +typedef void (*activate_func_t)(GtkWidget *, gint index); + +static void +add_menu_item(GtkWidget *menu, gchar *title, activate_func_t func, gint index, gint **imap) +{ + GtkWidget *item; + if (!menu || !title || !func) + return; + + item = gtk_menu_item_new_with_label(title); + gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)func, (gpointer) index); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + + if (imap) + *((*imap)++) = index; +} + +static void +gtk2_spin_button_hack(GtkSpinButton *spin) +{ + static gboolean entered = FALSE; + const gchar *text; + + if (entered) return; + entered = TRUE; + + text = gtk_entry_get_text(GTK_ENTRY(spin)); + if (text && *text) + { + gint value = atoi(text); + if (value != gtk_spin_button_get_value_as_int(spin)) + gtk_spin_button_set_value(spin, value); + } + else + { + gtk_spin_button_set_value(spin, 0.0); + gtk_entry_set_text(GTK_ENTRY(spin), ""); + } + + entered = FALSE; +} + +/*** output method ***********************************************************/ + +/*-- callbacks --------------------------------------------------------------*/ + +void +on_output_oss_radio_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + SET_PAGE("output_notebook", 0); + xfg->output_method = OUTPUT_METHOD_BUILTIN_OSS; +} + +void +on_output_plugin_radio_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + SET_PAGE("output_notebook", 1); + xfg->output_method = OUTPUT_METHOD_PLUGIN; +} + +void +on_output_none_radio_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + SET_PAGE("output_notebook", 2); + xfg->output_method = OUTPUT_METHOD_BUILTIN_NULL; +} + +static void +resampling_rate_cb(GtkWidget *widget, gint index) +{ + xfg->output_rate = index; +} + +#ifdef HAVE_LIBSAMPLERATE +static void +resampling_quality_cb(GtkWidget *widget, gint index) +{ + xfg->output_quality = index; +} +#endif + +/*** oss output **************************************************************/ + +static void +scan_devices(gchar *type, GtkWidget *option_menu, activate_func_t signal_f) +{ +#ifdef HAVE_OSS + gchar buffer[256]; + FILE *file; + + GtkWidget *item; + gboolean found = FALSE; + gint type_len = strlen(type); +#endif + + GtkWidget *menu; + gint index = 0; + + menu = gtk_menu_new(); + +#ifdef HAVE_OSS + /* look for devices in /dev/sndstat or /proc/asound/sndstat (OSS style) */ + if ((file = fopen("/dev/sndstat", "r")) || + (file = fopen("/proc/asound/sndstat", "r")) || + (file = fopen("/proc/asound/oss/sndstat", "r"))) + { + while (fgets(buffer, sizeof(buffer), file)) + { + gint i = strlen(buffer) - 1; + while ((i >= 0) && isspace(buffer[i])) + buffer[i--] = 0; + + if (found) + { + if (!buffer[0] || !isdigit(buffer[0])) + break; + + if (index == 0) + { + gchar *label, *p = strchr(buffer, ':'); + if (p) + while (*++p == ' '); + else + p = buffer; + + label = g_strdup_printf("Default (%s)", p); + item = gtk_menu_item_new_with_label(label); + g_free(label); + } + else + item = gtk_menu_item_new_with_label(buffer); + + gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)signal_f, (gpointer) index); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + index++; + } + else if (strcmp(buffer, type) == 0) + found = TRUE; + else if (strncmp(buffer, type, type_len) == 0) + DEBUG(("[crossfade] scan_devices: %s\n", buffer)); + } + fclose(file); + + if (!found) + DEBUG(("[crossfade] scan_devices: section \"%s\" not found!\n", type)); + } + else + { + DEBUG(("[crossfade] scan_devices: no sndstat found!\n")); +#ifdef SOUND_MIXER_INFO + /* from xmms-3dse7 by Frank Cornelis */ + DEBUG(("[crossfade] scan_devices: using alternate method...\n")); + for (;;) + { + gchar dev_name[32]; + int fd; + gint mixer = 0; + mixer_info info; + gchar *label; + + if (mixer != 0) + sprintf(dev_name, "/dev/mixer%d", mixer); + else + strcpy(dev_name, "/dev/mixer"); + + if ((fd = open(dev_name, O_RDONLY)) != -1) + { + if (ioctl(fd, SOUND_MIXER_INFO, &info) != -1) + { + label = g_strdup_printf(index ? "%s" : "Default (%s)", info.name); + add_menu_item(menu, label, signal_f, index, NULL); + g_free(label); + index++; + } + close(fd); + } + else + break; + mixer++; + } +#endif + } +#endif /* HAVE_OSS */ + + /* create default entry if no device(s) could be found */ + if (index == 0) + add_menu_item(menu, "Default", signal_f, 0, NULL); + + /* attach menu */ + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); +} + +/*-- oss output callbacks ---------------------------------------------------*/ + +void +check_oss_dependencies() +{ + if (checking) return; + checking = TRUE; + + SET_SENSITIVE("oss_adevice_optionmenu", !xfg->oss_use_alt_audio_device); + SET_SENSITIVE("oss_adevice_alt_entry", xfg->oss_use_alt_audio_device); + + SET_SENSITIVE("oss_mdevice_optionmenu", !xfg->oss_use_alt_mixer_device); + SET_SENSITIVE("oss_mdevice_alt_entry", xfg->oss_use_alt_mixer_device); + + SET_SENSITIVE("osshwb_fragments_label", !xfg->oss_maxbuf_enable); + SET_SENSITIVE("osshwb_fragments_spin", !xfg->oss_maxbuf_enable); + SET_SENSITIVE("osshwb_fragsize_label", !xfg->oss_maxbuf_enable); + SET_SENSITIVE("osshwb_fragsize_spin", !xfg->oss_maxbuf_enable); + + checking = FALSE; +} + +void +config_adevice_cb(GtkWidget *widget, gint device) +{ + xfg->oss_audio_device = device; +} + +void +config_mdevice_cb(GtkWidget *widget, gint device) +{ + xfg->oss_mixer_device = device; +} + +void +on_config_adevice_alt_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->oss_use_alt_audio_device = gtk_toggle_button_get_active(togglebutton); + check_oss_dependencies(); +} + +void +on_config_mdevice_alt_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->oss_use_alt_mixer_device = gtk_toggle_button_get_active(togglebutton); + check_oss_dependencies(); +} + +void +on_osshwb_maxbuf_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->oss_maxbuf_enable = gtk_toggle_button_get_active(togglebutton); + check_oss_dependencies(); +} + +/*** plugin output ***********************************************************/ + +static void config_plugin_cb(GtkWidget *widget, gint index); + +static gint +scan_plugins(GtkWidget *option_menu, gchar *selected) +{ + GtkWidget *menu = gtk_menu_new(); + GList *list = g_list_first(xfplayer_get_output_list()); + gint index = 0; + gint sel_index = -1; + gint def_index = -1; + + /* sanity check */ + if (selected == NULL) + selected = ""; + + /* parse module list */ + while (list) + { + OutputPlugin *op = (OutputPlugin *) list->data; + GtkWidget *item = gtk_menu_item_new_with_label(op->description); + + if (op == get_crossfade_oplugin_info()) /* disable selecting ourselves */ + gtk_widget_set_sensitive(item, FALSE); + else + { + if (def_index == -1) + def_index = index; + + if (op->filename && strcmp(g_basename(op->filename), selected) == 0) + sel_index = index; + } + + /* create menu item */ + gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)config_plugin_cb, (gpointer) index++); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + + /* advance to next module */ + list = g_list_next(list); + } + + /* attach menu */ + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + + if (sel_index == -1) + { + DEBUG(("[crossfade] scan_plugins: plugin not found (\"%s\")\n", selected)); + return def_index; /* use default (first entry) */ + } + return sel_index; +} + +/*-- plugin output callbacks ------------------------------------------------*/ + +static void +config_plugin_cb(GtkWidget *widget, gint index) +{ + OutputPlugin *op = g_list_nth_data(xfplayer_get_output_list(), index); + + /* get plugin options from gui */ + op_config.throttle_enable = GET_TOGGLE("op_throttle_check"); + op_config.max_write_enable = GET_TOGGLE("op_maxblock_check"); + op_config.max_write_len = GET_SPIN ("op_maxblock_spin"); + op_config.force_reopen = GET_TOGGLE("op_forcereopen_check"); + + /* config -> string */ + xfade_save_plugin_config(&xfg->op_config_string, xfg->op_name, &op_config); + + /* select new plugin */ + op_index = index; + + /* get new plugin's name */ + if (xfg->op_name) g_free(xfg->op_name); + xfg->op_name = (op && op->filename) ? g_strdup(g_basename(op->filename)) : NULL; + + /* string -> config */ + xfade_load_plugin_config(xfg->op_config_string, xfg->op_name, &op_config); + + /* update gui */ + SET_SENSITIVE("op_configure_button", op && (op->configure != NULL)); + SET_SENSITIVE("op_about_button", op && (op->about != NULL)); + SET_TOGGLE ("op_throttle_check", op_config.throttle_enable); + SET_TOGGLE ("op_maxblock_check", op_config.max_write_enable); + SET_SPIN ("op_maxblock_spin", op_config.max_write_len); + SET_SENSITIVE("op_maxblock_spin", op_config.max_write_enable); + SET_TOGGLE ("op_forcereopen_check", op_config.force_reopen); +} + +void +on_output_plugin_configure_button_clicked(GtkButton *button, gpointer user_data) +{ + OutputPlugin *op = g_list_nth_data(xfplayer_get_output_list(), op_index); + if ((op == NULL) || (op->configure == NULL)) + return; + + op->configure(); +} + +void +on_output_plugin_about_button_clicked(GtkButton *button, gpointer user_data) +{ + OutputPlugin *op = g_list_nth_data(xfplayer_get_output_list(), op_index); + if ((op == NULL) || (op->about == NULL)) + return; + + op->about(); +} + +void +on_op_throttle_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + op_config.throttle_enable = gtk_toggle_button_get_active(togglebutton); +} + +void +on_op_maxblock_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + op_config.max_write_enable = gtk_toggle_button_get_active(togglebutton); + SET_SENSITIVE("op_maxblock_spin", op_config.max_write_enable); +} + +void +on_op_maxblock_spin_changed(GtkEditable *editable, gpointer user_data) +{ + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + op_config.max_write_len = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); +} + +void +on_op_forcereopen_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + op_config.max_write_enable = gtk_toggle_button_get_active(togglebutton); +} + +/*** effects *****************************************************************/ + +static void config_effect_plugin_cb(GtkWidget *widget, gint index); + +static gint +scan_effect_plugins(GtkWidget *option_menu, gchar *selected) +{ + assert(xfplayer_get_effect_list()); + + GtkWidget *menu = gtk_menu_new(); + GList *list = g_list_first(xfplayer_get_effect_list()); + gint index = 0; + gint sel_index = -1; + gint def_index = -1; + + /* sanity check */ + if (selected == NULL) + selected = ""; + + /* parse module list */ + while (list) + { + EffectPlugin *ep = (EffectPlugin *) list->data; + GtkWidget *item = gtk_menu_item_new_with_label(ep->description); + + if (def_index == -1) + def_index = index; + + if (ep->filename && !strcmp(g_basename(ep->filename), selected)) + sel_index = index; + + /* create menu item */ + gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)config_effect_plugin_cb, (gpointer) index++); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + + /* advance to next module */ + list = g_list_next(list); + } + + /* attach menu */ + gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu); + + if (sel_index == -1) + { + DEBUG(("[crossfade] scan_effect_plugins: plugin not found (\"%s\")\n", selected)); + return def_index; /* use default (first entry) */ + } + return sel_index; +} + +/*-- plugin output callbacks ------------------------------------------------*/ + +static void +config_effect_plugin_cb(GtkWidget *widget, gint index) +{ + assert(xfplayer_get_effect_list()); + EffectPlugin *ep = g_list_nth_data(xfplayer_get_effect_list(), index); + + /* select new plugin */ + ep_index = index; + + /* get new plugin's name */ + if (xfg->ep_name) g_free(xfg->ep_name); + xfg->ep_name = (ep && ep->filename) ? g_strdup(g_basename(ep->filename)) : NULL; + + /* update gui */ + SET_SENSITIVE("ep_configure_button", ep && (ep->configure != NULL)); + SET_SENSITIVE("ep_about_button", ep && (ep->about != NULL)); + + /* 0.3.5: apply effect config immediatelly */ + if (config->ep_name) g_free(config->ep_name); + config->ep_name = g_strdup(xfg->ep_name); +} + +void +on_ep_configure_button_clicked(GtkButton *button, gpointer user_data) +{ + assert(xfplayer_get_effect_list()); + + EffectPlugin *ep = g_list_nth_data(xfplayer_get_effect_list(), ep_index); + if ((ep == NULL) || (ep->configure == NULL)) + return; + + ep->configure(); +} + +void +on_ep_about_button_clicked(GtkButton *button, gpointer user_data) +{ + assert(xfplayer_get_effect_list()); + + EffectPlugin *ep = g_list_nth_data(xfplayer_get_effect_list(), ep_index); + if ((ep == NULL) || (ep->about == NULL)) + return; + + ep->about(); +} + +void +on_ep_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + /* 0.3.5: apply effect config immediatelly */ + config->ep_enable = xfg->ep_enable = GET_TOGGLE("ep_enable_check"); +} + +/*-- volume normalizer ------------------------------------------------------*/ + +void +check_effects_dependencies() +{ + if (checking) return; + checking = TRUE; + + SET_SENSITIVE("volnorm_target_spin", xfg->volnorm_enable); + SET_SENSITIVE("volnorm_target_label", xfg->volnorm_enable); + SET_SENSITIVE("volnorm_quantaudio_check", xfg->volnorm_enable); + SET_SENSITIVE("volnorm_target_spin", xfg->volnorm_enable); + + checking = FALSE; +} + +void +on_volnorm_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->volnorm_enable = gtk_toggle_button_get_active(togglebutton); + check_effects_dependencies(); +} + +/*** crossfader **************************************************************/ + +static void xf_config_cb(GtkWidget *widget, gint index); +static void xf_type_cb (GtkWidget *widget, gint index); + +/* crude hack to keep track of menu items */ +static gint xf_config_index_map[MAX_FADE_CONFIGS]; +static gint xf_type_index_map [MAX_FADE_TYPES]; + +static void +create_crossfader_config_menu() +{ + GtkWidget *optionmenu, *menu; + gint i, *imap; + + if ((optionmenu = lookup_widget(config_win, "xf_config_optionmenu"))) + { + for (i = 0; i < MAX_FADE_CONFIGS; i++) + xf_config_index_map[i] = -1; + + imap = xf_config_index_map; + menu = gtk_menu_new(); + + add_menu_item(menu, "Start of playback", xf_config_cb, FADE_CONFIG_START, &imap); + add_menu_item(menu, "Automatic songchange", xf_config_cb, FADE_CONFIG_XFADE, &imap); +#if 0 + /* this should be FADE_TYPE_NONE all the time, anyway, + so no need to make it configureable by the user */ + add_menu_item(menu, "Automatic (gapless)", xf_config_cb, FADE_CONFIG_ALBUM, &imap); +#endif + add_menu_item(menu, "Manual songchange", xf_config_cb, FADE_CONFIG_MANUAL, &imap); + add_menu_item(menu, "Manual stop", xf_config_cb, FADE_CONFIG_STOP, &imap); + add_menu_item(menu, "End of playlist", xf_config_cb, FADE_CONFIG_EOP, &imap); + add_menu_item(menu, "Seeking", xf_config_cb, FADE_CONFIG_SEEK, &imap); + add_menu_item(menu, "Pause", xf_config_cb, FADE_CONFIG_PAUSE, &imap); + gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu); + } + +} + +static void +create_crossfader_type_menu() +{ + GtkWidget *optionmenu, *menu; + gint i, *imap; + guint32 mask; + + if ((optionmenu = lookup_widget(config_win, "xf_type_optionmenu"))) + { + for (i = 0; i < MAX_FADE_TYPES; i++) + xf_type_index_map[i] = -1; + + imap = xf_type_index_map; + menu = gtk_menu_new(); + mask = xfg->fc[xfg->xf_index].type_mask; + + if (mask & (1 << FADE_TYPE_REOPEN)) add_menu_item(menu, "Reopen output device", xf_type_cb, FADE_TYPE_REOPEN, &imap); + if (mask & (1 << FADE_TYPE_FLUSH)) add_menu_item(menu, "Flush output device", xf_type_cb, FADE_TYPE_FLUSH, &imap); + if (mask & (1 << FADE_TYPE_NONE)) add_menu_item(menu, "None (gapless/off)", xf_type_cb, FADE_TYPE_NONE, &imap); + if (mask & (1 << FADE_TYPE_PAUSE)) add_menu_item(menu, "Pause", xf_type_cb, FADE_TYPE_PAUSE, &imap); + if (mask & (1 << FADE_TYPE_SIMPLE_XF)) add_menu_item(menu, "Simple crossfade", xf_type_cb, FADE_TYPE_SIMPLE_XF, &imap); + if (mask & (1 << FADE_TYPE_ADVANCED_XF)) add_menu_item(menu, "Advanced crossfade", xf_type_cb, FADE_TYPE_ADVANCED_XF, &imap); + if (mask & (1 << FADE_TYPE_FADEIN)) add_menu_item(menu, "Fadein", xf_type_cb, FADE_TYPE_FADEIN, &imap); + if (mask & (1 << FADE_TYPE_FADEOUT)) add_menu_item(menu, "Fadeout", xf_type_cb, FADE_TYPE_FADEOUT, &imap); + if (mask & (1 << FADE_TYPE_PAUSE_NONE)) add_menu_item(menu, "None", xf_type_cb, FADE_TYPE_PAUSE_NONE, &imap); + if (mask & (1 << FADE_TYPE_PAUSE_ADV)) add_menu_item(menu, "Fadeout/Fadein", xf_type_cb, FADE_TYPE_PAUSE_ADV, &imap); + gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu); + } +} + +#define NONE 0x00000000L +#define XF_CONFIG 0x00000001L +#define XF_TYPE 0x00000002L +#define XF_MIX_SIZE 0x00000004L +#define XF_FADEOUT 0x00000008L +#define XF_OFFSET 0x00000010L +#define XF_FADEIN 0x00000020L +#define XF_PAGE 0x00000040L +#define XF_FLUSH 0x00000080L +#define ANY 0xffffffffL + +static void +check_crossfader_dependencies(guint32 mask) +{ + fade_config_t *fc = &xfg->fc[xfg->xf_index]; + gint i; + + /* HACK: avoid endless recursion */ + if (checking) return; + checking = TRUE; + + if (mask & XF_FLUSH) + { + SET_TOGGLE("xftfp_enable_check", fc->flush_pause_enable); + SET_SENSITIVE("xftfp_length_label", fc->flush_pause_enable); + SET_SENSITIVE("xftfp_length_spin", fc->flush_pause_enable); + SET_TOGGLE("xftffi_enable_check", fc->flush_in_enable); + SET_SENSITIVE("xftffi_length_label", fc->flush_in_enable); + SET_SENSITIVE("xftffi_length_spin", fc->flush_in_enable); + SET_SENSITIVE("xftffi_volume_label", fc->flush_in_enable); + SET_SENSITIVE("xftffi_volume_spin", fc->flush_in_enable); + } + + if (mask & XF_MIX_SIZE) + { + SET_TOGGLE("xf_autobuf_check", xfg->mix_size_auto); + SET_SENSITIVE("xf_buffer_spin", !xfg->mix_size_auto); + SET_SPIN("xf_buffer_spin", xfade_mix_size_ms(xfg)); + } + + if (mask & XF_CONFIG) + { + for (i = 0; i < MAX_FADE_CONFIGS && (xf_config_index_map[i] != xfg->xf_index); i++); + if (i == MAX_FADE_CONFIGS) + i = 0; + SET_HISTORY("xf_config_optionmenu", i); + } + + if (mask & XF_TYPE) + { + create_crossfader_type_menu(); + for (i = 0; i < MAX_FADE_TYPES && (xf_type_index_map[i] != fc->type); i++); + if (i == MAX_FADE_TYPES) + { + fc->type = FADE_TYPE_NONE; + for (i = 0; i < MAX_FADE_TYPES && (xf_type_index_map[i] != fc->type); i++); + if (i == MAX_FADE_CONFIGS) + i = 0; + } + SET_HISTORY("xf_type_optionmenu", i); + } + + if (mask & XF_PAGE) + { + SET_PAGE("xf_type_notebook", fc->type); + SET_SPIN("pause_length_spin", fc->pause_len_ms); + SET_SPIN("simple_length_spin", fc->simple_len_ms); + if (fc->config == FADE_CONFIG_SEEK) + { + HIDE("xftf_pause_frame"); + HIDE("xftf_fadein_frame"); + } + else + { + SHOW("xftf_pause_frame"); + SHOW("xftf_fadein_frame"); + } + } + + if (mask & XF_FADEOUT) + { + SET_TOGGLE("fadeout_enable_check", fc->out_enable); + SET_SENSITIVE("fadeout_length_label", fc->out_enable); + SET_SENSITIVE("fadeout_length_spin", fc->out_enable); + SET_SPIN("fadeout_length_spin", fc->out_len_ms); + SET_SENSITIVE("fadeout_volume_label", fc->out_enable); + SET_SENSITIVE("fadeout_volume_spin", fc->out_enable); + SET_SPIN("fadeout_volume_spin", fc->out_volume); + SET_SPIN("xftfo_length_spin", fc->out_len_ms); + SET_SPIN("xftfo_volume_spin", fc->out_volume); + SET_SPIN("xftfoi_fadeout_spin", fc->out_len_ms); + } + + if (mask & XF_FADEIN) + { + SET_TOGGLE("fadein_lock_check", fc->in_locked); + SET_SENSITIVE("fadein_enable_check", !fc->in_locked); + SET_TOGGLE("fadein_enable_check", fc->in_locked ? fc->out_enable : fc->in_enable); + SET_SENSITIVE("fadein_length_label", !fc->in_locked && fc->in_enable); + SET_SENSITIVE("fadein_length_spin", !fc->in_locked && fc->in_enable); + SET_SPIN("fadein_length_spin", fc->in_locked ? fc->out_len_ms : fc->in_len_ms); + SET_SENSITIVE("fadein_volume_label", !fc->in_locked && fc->in_enable); + SET_SENSITIVE("fadein_volume_spin", !fc->in_locked && fc->in_enable); + SET_SPIN("fadein_volume_spin", fc->in_locked ? fc->out_volume : fc->in_volume); + SET_SPIN("xftfi_length_spin", fc->in_len_ms); + SET_SPIN("xftfi_volume_spin", fc->in_volume); + SET_SPIN("xftfoi_fadein_spin", fc->in_len_ms); + } + + if (mask & XF_OFFSET) + { + if (fc->out_enable) + SET_SENSITIVE("xfofs_lockout_radiobutton", TRUE); + if (!fc->in_locked && fc->in_enable) + SET_SENSITIVE("xfofs_lockin_radiobutton", TRUE); + + switch (fc->ofs_type) + { + case FC_OFFSET_LOCK_OUT: + if (!fc->out_enable) + { + SET_TOGGLE("xfofs_none_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_NONE; + } + break; + + case FC_OFFSET_LOCK_IN: + if (!(!fc->in_locked && fc->in_enable)) + { + if ((fc->in_locked && fc->out_enable)) + { + SET_TOGGLE("xfofs_lockout_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_LOCK_OUT; + } + else + { + SET_TOGGLE("xfofs_none_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_NONE; + } + } + break; + } + + switch (fc->ofs_type_wanted) + { + case FC_OFFSET_NONE: + SET_TOGGLE("xfofs_none_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_NONE; + break; + + case FC_OFFSET_LOCK_OUT: + if (fc->out_enable) + { + SET_TOGGLE("xfofs_lockout_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_LOCK_OUT; + } + break; + + case FC_OFFSET_LOCK_IN: + if (!fc->in_locked && fc->in_enable) + { + SET_TOGGLE("xfofs_lockin_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_LOCK_IN; + } + else if (fc->out_enable) + { + SET_TOGGLE("xfofs_lockout_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_LOCK_OUT; + } + break; + + case FC_OFFSET_CUSTOM: + SET_TOGGLE("xfofs_custom_radiobutton", TRUE); + fc->ofs_type = FC_OFFSET_CUSTOM; + break; + } + + if (!fc->out_enable) + SET_SENSITIVE("xfofs_lockout_radiobutton", FALSE); + if (!(!fc->in_locked && fc->in_enable)) + SET_SENSITIVE("xfofs_lockin_radiobutton", FALSE); + + SET_SENSITIVE("xfofs_custom_spin", fc->ofs_type == FC_OFFSET_CUSTOM); + SET_SPIN("xfofs_custom_spin", xfade_cfg_offset(fc)); + SET_SPIN("xftfo_silence_spin", xfade_cfg_offset(fc)); + SET_SPIN("xftfoi_silence_spin", xfade_cfg_offset(fc)); + } + + checking = FALSE; +} + +/*-- crossfader callbacks ---------------------------------------------------*/ + +void +on_xf_buffer_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->mix_size_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(NONE); +} + +void +on_xf_autobuf_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + xfg->mix_size_auto = gtk_toggle_button_get_active(togglebutton); + check_crossfader_dependencies(XF_MIX_SIZE); +} + +/* - config/type - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +xf_config_cb(GtkWidget *widget, gint index) +{ + if (checking) return; + xfg->xf_index = index; + check_crossfader_dependencies(ANY & ~XF_CONFIG); +} + +void +xf_type_cb(GtkWidget *widget, gint index) +{ + if (checking) return; + xfg->fc[xfg->xf_index].type = index; + check_crossfader_dependencies(ANY & ~XF_CONFIG); +} + +/* - flush - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +on_xftfp_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].flush_pause_enable = gtk_toggle_button_get_active(togglebutton); + check_crossfader_dependencies(XF_FLUSH | XF_MIX_SIZE); +} + +void +on_xftfp_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].flush_pause_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_FLUSH); +} + +void +on_xftffi_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].flush_in_enable = gtk_toggle_button_get_active(togglebutton); + check_crossfader_dependencies(XF_FLUSH | XF_OFFSET | XF_FADEOUT | XF_FADEIN); +} + +void +on_xftffi_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].flush_in_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_FLUSH); +} + +void +on_xftffi_volume_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].flush_in_volume = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_FLUSH); +} + +/* - pause - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +on_pause_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].pause_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_MIX_SIZE); +} + +/* - simple - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +on_simple_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].simple_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_MIX_SIZE); +} + +/* - crossfade-fadeout - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +on_fadeout_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].out_enable = gtk_toggle_button_get_active(togglebutton); + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET | XF_FADEOUT | XF_FADEIN); +} + +void +on_fadeout_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].out_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET | XF_FADEIN); +} + +void +on_fadeout_volume_spin_changed(GtkEditable *editable, gpointer user_data) +{ + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].out_volume = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_OFFSET | XF_FADEIN); +} + +/* - crossfade-offset - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +on_xfofs_none_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking || !gtk_toggle_button_get_active(togglebutton)) return; + xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_NONE; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_NONE; + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET); +} + +void +on_xfofs_none_radiobutton_clicked(GtkButton *button, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_NONE; +} + +void +on_xfofs_lockout_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking || !gtk_toggle_button_get_active(togglebutton)) return; + xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_LOCK_OUT; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_OUT; + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET); +} + +void +on_xfofs_lockout_radiobutton_clicked(GtkButton *button, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_OUT; +} + +void +on_xfofs_lockin_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking || !gtk_toggle_button_get_active(togglebutton)) return; + xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_LOCK_IN; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_IN; + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET); +} + +void +on_xfofs_lockin_radiobutton_clicked(GtkButton *button, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_IN; +} + +void +on_xfofs_custom_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking || !gtk_toggle_button_get_active(togglebutton)) return; + xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_CUSTOM; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_CUSTOM; + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET); +} + +void +on_xfofs_custom_radiobutton_clicked(GtkButton *button, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_CUSTOM; +} + +void +on_xfofs_custom_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].ofs_custom_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_MIX_SIZE); +} + +/* - crossfade-fadein - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +void +on_fadein_lock_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].in_locked = gtk_toggle_button_get_active(togglebutton); + check_crossfader_dependencies(XF_OFFSET | XF_FADEIN); +} + +void +on_fadein_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->fc[xfg->xf_index].in_enable = gtk_toggle_button_get_active(togglebutton); + check_crossfader_dependencies(XF_OFFSET | XF_FADEIN); +} + +void +on_fadein_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].in_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET); +} + +void +on_fadein_volume_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->fc[xfg->xf_index].in_volume = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_crossfader_dependencies(NONE); +} + +/*-- fadein -----------------------------------------------------------------*/ + +/* signal set to on_fadein_length_spin_changed */ +/* signal set to on_fadein_volume_spin_changed */ + +/*-- fadeout ----------------------------------------------------------------*/ + +/* signal set to on_fadeout_length_spin_changed */ +/* signal set to on_fadeout_volume_spin_changed */ + +/*-- fadeout/fadein ---------------------------------------------------------*/ + +/* signal set to on_fadeout_length_spin_changed */ +/* signal set to on_xfofs_custom_spin_changed */ +/* signal set to on_fadeout_volume_spin_changed */ + +/*** gap killer **************************************************************/ + +void +check_gapkiller_dependencies() +{ + if (checking) return; + checking = TRUE; + + SET_SENSITIVE("lgap_length_spin", xfg->gap_lead_enable); + SET_SENSITIVE("lgap_level_spin", xfg->gap_lead_enable); + SET_SENSITIVE("tgap_enable_check", !xfg->gap_trail_locked); + SET_SENSITIVE("tgap_length_spin", !xfg->gap_trail_locked && xfg->gap_trail_enable); + SET_SENSITIVE("tgap_level_spin", !xfg->gap_trail_locked && xfg->gap_trail_enable); + + if (xfg->gap_trail_locked) + { + SET_TOGGLE("tgap_enable_check", xfg->gap_lead_enable); + SET_SPIN("tgap_length_spin", xfg->gap_lead_len_ms); + SET_SPIN("tgap_level_spin", xfg->gap_lead_level); + } + else + { + SET_TOGGLE("tgap_enable_check", xfg->gap_trail_enable); + SET_SPIN("tgap_length_spin", xfg->gap_trail_len_ms); + SET_SPIN("tgap_level_spin", xfg->gap_trail_level); + } + + if (xfg->mix_size_auto) + SET_SPIN("xf_buffer_spin", xfade_mix_size_ms(xfg)); + + checking = FALSE; +} + +/*-- gapkiller callbacks ----------------------------------------------------*/ + +void +on_lgap_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->gap_lead_enable = gtk_toggle_button_get_active(togglebutton); + check_gapkiller_dependencies(); +} + +void +on_lgap_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->gap_lead_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_gapkiller_dependencies(); +} + +void +on_lgap_level_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->gap_lead_level = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_gapkiller_dependencies(); +} + +void +on_tgap_lock_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->gap_trail_locked = gtk_toggle_button_get_active(togglebutton); + check_gapkiller_dependencies(); +} + +void +on_tgap_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->gap_trail_enable = gtk_toggle_button_get_active(togglebutton); + check_gapkiller_dependencies(); +} + +void +on_tgap_length_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->gap_trail_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); +} + +void +on_tgap_level_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->gap_trail_level = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); +} + +/*** misc ********************************************************************/ + +void +check_misc_dependencies() +{ + if (checking) return; + checking = TRUE; + + if (xfg->mix_size_auto) + SET_SPIN("xf_buffer_spin", xfade_mix_size_ms(xfg)); + + SET_SENSITIVE("moth_opmaxused_spin", xfg->enable_op_max_used); + + checking = FALSE; +} + +/*-- misc callbacks ---------------------------------------------------------*/ + +void +on_config_mixopt_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + SET_SENSITIVE("mixopt_reverse_check", gtk_toggle_button_get_active(togglebutton)); + SET_SENSITIVE("mixopt_software_check", gtk_toggle_button_get_active(togglebutton)); +} + +void +on_moth_songchange_spin_changed(GtkEditable *editable, gpointer user_data) +{ + if (checking) return; + gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable)); + xfg->songchange_timeout = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable)); + check_misc_dependencies(); +} + +void +on_moth_opmaxused_check_toggled(GtkToggleButton *togglebutton, gpointer user_data) +{ + if (checking) return; + xfg->enable_op_max_used = gtk_toggle_button_get_active(togglebutton); + check_misc_dependencies(); +} + +/*** main config *************************************************************/ + +void +on_config_apply_clicked(GtkButton *button, gpointer user_data) +{ + GtkWidget *widget; + + /* get current notebook page */ + if ((widget = lookup_widget(config_win, "config_notebook"))) + xfg->page = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); + + /* output method */ + + /* sample rate */ + + /* output method: builtin OSS */ + if ((widget = lookup_widget(config_win, "output_oss_notebook"))) + xfg->oss_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); + + if ((widget = lookup_widget(config_win, "oss_adevice_alt_entry"))) + { + if (xfg->oss_alt_audio_device) + g_free(xfg->oss_alt_audio_device); + xfg->oss_alt_audio_device = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1); + g_strstrip(xfg->oss_alt_audio_device); + } + + if ((widget = lookup_widget(config_win, "oss_mdevice_alt_entry"))) + { + if (xfg->oss_alt_mixer_device) + g_free(xfg->oss_alt_mixer_device); + xfg->oss_alt_mixer_device = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1); + g_strstrip(xfg->oss_alt_mixer_device); + } + + xfg->oss_buffer_size_ms = GET_SPIN("ossbuf_buffer_spin"); + xfg->oss_preload_size_ms = GET_SPIN("ossbuf_preload_spin"); + + xfg->oss_fragments = GET_SPIN("osshwb_fragments_spin"); + xfg->oss_fragment_size = GET_SPIN("osshwb_fragsize_spin"); + xfg->oss_maxbuf_enable = GET_TOGGLE("osshwb_maxbuf_check"); + + xfg->oss_mixer_use_master = GET_TOGGLE("ossmixer_pcm_check"); + + /* output method: plugin */ + op_config.throttle_enable = GET_TOGGLE("op_throttle_check"); + op_config.max_write_enable = GET_TOGGLE("op_maxblock_check"); + op_config.max_write_len = GET_SPIN("op_maxblock_spin"); + op_config.force_reopen = GET_TOGGLE("op_forcereopen_check"); + + xfade_save_plugin_config(&xfg->op_config_string, xfg->op_name, &op_config); + + /* output method: none: */ + + /* effects: pre-mixing effect plugin */ + + /* effects: volume normalizer */ + xfg->volnorm_target = GET_SPIN("volnorm_target_spin"); + xfg->volnorm_use_qa = GET_TOGGLE("volnorm_quantaudio_check"); + + /* crossfader */ + xfg->mix_size_auto = GET_TOGGLE("xf_autobuf_check"); + + /* gap killer */ + xfg->gap_lead_enable = GET_TOGGLE("lgap_enable_check"); + xfg->gap_lead_len_ms = GET_SPIN("lgap_length_spin"); + xfg->gap_lead_level = GET_SPIN("lgap_level_spin"); + + xfg->gap_trail_locked = GET_TOGGLE("tgap_lock_check"); + + xfg->gap_crossing = GET_TOGGLE("gadv_crossing_check"); + + /* misc */ + xfg->enable_debug = GET_TOGGLE("debug_stderr_check"); + xfg->enable_monitor = GET_TOGGLE("debug_monitor_check"); + xfg->enable_mixer = GET_TOGGLE("mixopt_enable_check"); + xfg->mixer_reverse = GET_TOGGLE("mixopt_reverse_check"); + xfg->mixer_software = GET_TOGGLE("mixopt_software_check"); + xfg->preload_size_ms = GET_SPIN("moth_preload_spin"); + xfg->album_detection = GET_TOGGLE("noxf_album_check"); + xfg->no_xfade_if_same_file = GET_TOGGLE("noxf_samefile_check"); + xfg->enable_http_workaround = GET_TOGGLE("moth_httpworkaround_check"); + xfg->op_max_used_ms = GET_SPIN("moth_opmaxused_spin"); + xfg->output_keep_opened = GET_TOGGLE("moth_outputkeepopened_check"); + + /* presets */ + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + /* free existing strings */ + if (config->oss_alt_audio_device) g_free(config->oss_alt_audio_device); + if (config->oss_alt_mixer_device) g_free(config->oss_alt_mixer_device); + if (config->op_config_string) g_free(config->op_config_string); + if (config->op_name) g_free(config->op_name); + if (config->ep_name) g_free(config->ep_name); + + /* copy current settings (dupping the strings) */ + *config = *xfg; + config->oss_alt_audio_device = g_strdup(xfg->oss_alt_audio_device); + config->oss_alt_mixer_device = g_strdup(xfg->oss_alt_mixer_device); + config->op_config_string = g_strdup(xfg->op_config_string); + config->op_name = g_strdup(xfg->op_name); + config->ep_name = g_strdup(xfg->ep_name); + + /* tell the engine that the config has changed */ + xfade_realize_config(); + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + + /* save configuration */ + xfade_save_config(); + + /* show/hide monitor win depending on config->enable_monitor */ + xfade_check_monitor_win(); +} + +void +on_config_ok_clicked(GtkButton *button, gpointer user_data) +{ + /* apply and save config */ + on_config_apply_clicked(button, user_data); + + /* close and destroy window */ + gtk_widget_destroy(config_win); +} + +void +xfade_configure() +{ + GtkWidget *widget; + + if (!config_win) + { + /* create */ + if (!(config_win = create_config_win())) + { + DEBUG(("[crossfade] plugin_configure: error creating window!\n")); + return; + } + + /* update config_win when window is destroyed */ + gtk_signal_connect(GTK_OBJECT(config_win), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &config_win); + + /* free any strings that might be left in our local copy of the config */ + if (xfg->oss_alt_audio_device) g_free(xfg->oss_alt_audio_device); + if (xfg->oss_alt_mixer_device) g_free(xfg->oss_alt_mixer_device); + if (xfg->op_config_string) g_free(xfg->op_config_string); + if (xfg->op_name) g_free(xfg->op_name); + if (xfg->ep_name) g_free(xfg->ep_name); + + /* copy current settings (dupping the strings) */ + *xfg = *config; + xfg->oss_alt_audio_device = g_strdup(config->oss_alt_audio_device); + xfg->oss_alt_mixer_device = g_strdup(config->oss_alt_mixer_device); + xfg->op_config_string = g_strdup(config->op_config_string); + xfg->op_name = g_strdup(config->op_name); + xfg->ep_name = g_strdup(config->ep_name); + + /* go to remembered notebook page */ + if ((widget = lookup_widget(config_win, "config_notebook"))) + gtk_notebook_set_page(GTK_NOTEBOOK(widget), config->page); + + /* output: method */ +#ifdef HAVE_OSS + SET_SENSITIVE("output_oss_radio", TRUE); +#else + SET_SENSITIVE("output_oss_radio", FALSE); +#endif + + switch (xfg->output_method) + { + case OUTPUT_METHOD_BUILTIN_OSS: + widget = lookup_widget(config_win, "output_oss_radio"); + break; + + case OUTPUT_METHOD_PLUGIN: + widget = lookup_widget(config_win, "output_plugin_radio"); + break; + + case OUTPUT_METHOD_BUILTIN_NULL: + widget = lookup_widget(config_win, "output_none_radio"); + break; + + default: + widget = NULL; + } + if (widget) + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE); + + if ((widget = lookup_widget(config_win, "output_notebook"))) + gtk_notebook_set_page(GTK_NOTEBOOK(widget), xfg->output_method); + + /* output: resampling rate */ + if ((widget = lookup_widget(config_win, "resampling_rate_optionmenu"))) + { + GtkWidget *menu = gtk_menu_new(); + GtkWidget *item; + gint index, *rate; + char label[16]; + + for (rate = &sample_rates[0]; *rate; rate++) + { + g_snprintf(label, sizeof(label), "%d Hz", *rate); + item = gtk_menu_item_new_with_label(label); + gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)resampling_rate_cb, (gpointer)*rate); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + } + gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu); + + /* find list index for xfg->output_rate */ + for (rate = &sample_rates[0], index = 0; *rate && *rate != xfg->output_rate; rate++, index++); + + /* if the specified rate is not in the list of available rates, select default rate */ + if (!*rate) + { + DEBUG(("[crossfade] plugin_configure: WARNING: invalid output sample rate (%d)!\n", xfg->output_rate)); + DEBUG(("[crossfade] plugin_configure: ... using default of 44100\n")); + for (rate = &sample_rates[0], index = 0; *rate && *rate != 44100; rate++, index++); + } + + /* finally, set the list selection */ + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), index); + } + + /* output: resampling quality (libsamplerate setting) */ +#ifdef HAVE_LIBSAMPLERATE + if ((widget = lookup_widget(config_win, "resampling_quality_optionmenu"))) + { + GtkWidget *menu = gtk_menu_new(); + GtkWidget *item; + + GtkTooltips *tooltips = (GtkTooltips *) gtk_object_get_data(GTK_OBJECT(config_win), "tooltips"); + + int converter_type; + const char *name, *description; + for (converter_type = 0; (name = src_get_name(converter_type)); converter_type++) + { + description = src_get_description(converter_type); + + item = gtk_menu_item_new_with_label(name); + gtk_tooltips_set_tip(tooltips, item, description, NULL); + + gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)resampling_quality_cb, (gpointer) converter_type); + gtk_widget_show(item); + gtk_menu_append(GTK_MENU(menu), item); + } + + gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), xfg->output_quality); + } +#else + HIDE("resampling_quality_hbox"); + HIDE("resampling_quality_optionmenu"); +#endif + + /* output method: builtin OSS */ + if ((widget = lookup_widget(config_win, "output_oss_notebook"))) + gtk_notebook_set_page(GTK_NOTEBOOK(widget), xfg->oss_page); + + if ((widget = lookup_widget(config_win, "oss_adevice_optionmenu"))) + { + scan_devices("Audio devices:", widget, config_adevice_cb); + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), xfg->oss_audio_device); + gtk_widget_set_sensitive(widget, !xfg->oss_use_alt_audio_device); + } + SET_TOGGLE("oss_adevice_alt_check", xfg->oss_use_alt_audio_device); + if ((widget = lookup_widget(config_win, "oss_adevice_alt_entry"))) + { + gtk_entry_set_text(GTK_ENTRY(widget), xfg->oss_alt_audio_device ? xfg->oss_alt_audio_device : DEFAULT_OSS_ALT_AUDIO_DEVICE); + gtk_widget_set_sensitive(widget, xfg->oss_use_alt_audio_device); + } + + if ((widget = lookup_widget(config_win, "oss_mdevice_optionmenu"))) + { + scan_devices("Mixers:", widget, config_mdevice_cb); + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), xfg->oss_mixer_device); + gtk_widget_set_sensitive(widget, !xfg->oss_use_alt_mixer_device); + } + SET_TOGGLE("oss_mdevice_alt_check", xfg->oss_use_alt_mixer_device); + if ((widget = lookup_widget(config_win, "oss_mdevice_alt_entry"))) + { + gtk_entry_set_text(GTK_ENTRY(widget), xfg->oss_alt_mixer_device ? xfg->oss_alt_mixer_device : DEFAULT_OSS_ALT_MIXER_DEVICE); + gtk_widget_set_sensitive(widget, xfg->oss_use_alt_mixer_device); + } + + SET_SPIN("ossbuf_buffer_spin", xfg->oss_buffer_size_ms); + SET_SPIN("ossbuf_preload_spin", xfg->oss_preload_size_ms); + + SET_SPIN("osshwb_fragments_spin", xfg->oss_fragments); + SET_SPIN("osshwb_fragsize_spin", xfg->oss_fragment_size); + SET_TOGGLE("osshwb_maxbuf_check", xfg->oss_maxbuf_enable); + + SET_TOGGLE("ossmixer_pcm_check", xfg->oss_mixer_use_master); + + check_oss_dependencies(); + + /* output method: plugin */ + xfade_load_plugin_config(xfg->op_config_string, xfg->op_name, &op_config); + SET_TOGGLE ("op_throttle_check", op_config.throttle_enable); + SET_TOGGLE ("op_maxblock_check", op_config.max_write_enable); + SET_SPIN ("op_maxblock_spin", op_config.max_write_len); + SET_SENSITIVE("op_maxblock_spin", op_config.max_write_enable); + SET_TOGGLE ("op_forcereopen_check", op_config.force_reopen); + + if ((widget = lookup_widget(config_win, "op_plugin_optionmenu"))) + { + OutputPlugin *op = NULL; + if ((op_index = scan_plugins(widget, xfg->op_name)) >= 0) + { + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), op_index); + op = g_list_nth_data(xfplayer_get_output_list(), op_index); + } + SET_SENSITIVE("op_configure_button", op && (op->configure != NULL)); + SET_SENSITIVE("op_about_button", op && (op->about != NULL)); + } + + /* output method: none */ + + /* effects: pre-mixing effect plugin */ + if (!xfplayer_get_effect_list()) + HIDE("config_effects_page") + else if ((widget = lookup_widget(config_win, "ep_plugin_optionmenu"))) + { + EffectPlugin *ep = NULL; + if ((ep_index = scan_effect_plugins(widget, xfg->ep_name)) >= 0) + { + gtk_option_menu_set_history(GTK_OPTION_MENU(widget), ep_index); + ep = g_list_nth_data(xfplayer_get_effect_list(), ep_index); + } + SET_SENSITIVE("ep_configure_button", ep && (ep->configure != NULL)); + SET_SENSITIVE("ep_about_button", ep && (ep->about != NULL)); + SET_TOGGLE ("ep_enable_check", xfg->ep_enable); + SET_SENSITIVE("ep_enable_check", ep_index != -1); + SET_SENSITIVE("ep_plugin_optionmenu", ep_index != -1); + } + + /* effects: volume normalizer */ + SET_TOGGLE("volnorm_enable_check", xfg->volnorm_enable); + SET_TOGGLE("volnorm_quantaudio_check", xfg->volnorm_use_qa); + SET_SPIN ("volnorm_target_spin", xfg->volnorm_target); + + check_effects_dependencies(); + + /* crossfader */ + create_crossfader_config_menu(); + + if ((xfg->xf_index < 0) || (xfg->xf_index >= MAX_FADE_CONFIGS)) + { + DEBUG(("[crossfade] plugin_configure: crossfade index out of range (%d)!\n", xfg->xf_index)); + xfg->xf_index = CLAMP(xfg->xf_index, 0, MAX_FADE_CONFIGS); + } + + check_crossfader_dependencies(ANY); + + /* gap killer */ + SET_TOGGLE("lgap_enable_check", xfg->gap_lead_enable); + SET_SPIN ("lgap_length_spin", xfg->gap_lead_len_ms); + SET_SPIN ("lgap_level_spin", xfg->gap_lead_level); + SET_TOGGLE("tgap_lock_check", xfg->gap_trail_locked); + SET_TOGGLE("tgap_enable_check", xfg->gap_trail_enable); + SET_SPIN ("tgap_length_spin", xfg->gap_trail_len_ms); + SET_SPIN ("tgap_level_spin", xfg->gap_trail_level); + SET_TOGGLE("gadv_crossing_check", xfg->gap_crossing); + + check_gapkiller_dependencies(); + + /* misc */ + SET_TOGGLE("debug_stderr_check", xfg->enable_debug); + SET_TOGGLE("debug_monitor_check", xfg->enable_monitor); + SET_TOGGLE("mixopt_enable_check", xfg->enable_mixer); + SET_TOGGLE("mixopt_reverse_check", xfg->mixer_reverse); + SET_TOGGLE("mixopt_software_check", xfg->mixer_software); + SET_SPIN ("moth_songchange_spin", xfg->songchange_timeout); + SET_SPIN ("moth_preload_spin", xfg->preload_size_ms); + SET_TOGGLE("noxf_album_check", xfg->album_detection); + SET_TOGGLE("noxf_samefile_check", xfg->album_detection); + SET_TOGGLE("moth_httpworkaround_check", xfg->enable_http_workaround); + SET_TOGGLE("moth_opmaxused_check", xfg->enable_op_max_used); + SET_SPIN ("moth_opmaxused_spin", xfg->op_max_used_ms); + SET_TOGGLE("moth_outputkeepopened_check", xfg->output_keep_opened); + + check_misc_dependencies(); + + /* presets */ + if ((set_wgt = lookup_widget(config_win, "presets_list_list"))) + { + GList *item; + + for (item = config->presets; item; item = g_list_next(item)) + { + gchar *name = (gchar *) item->data; + gchar *text[] = { name, "Default", "No" }; + gtk_clist_append(GTK_CLIST(set_wgt), text); + } + } + + /* show window near mouse pointer */ + gtk_window_set_position(GTK_WINDOW(config_win), GTK_WIN_POS_MOUSE); + gtk_widget_show(config_win); + } + else + /* bring window to front */ + gdk_window_raise(config_win->window); +} + +void +xfade_about() +{ + if (!about_win) + { + gchar *about_text = + "Audacious Crossfade Plugin\n" + "Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org>\n" + "\n" + "based on the original OSS Output Plugin Copyright (C) 1998-2000\n" + "Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies\n" + "\n" + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n" "USA."; + + about_win = create_about_win(); + + /* update about_win when window is destroyed */ + gtk_signal_connect(GTK_OBJECT(about_win), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &about_win); + + /* set about box text (this is done here and not in interface.c because + of the VERSION #define -- there is no way to do this with GLADE */ + if ((set_wgt = lookup_widget(about_win, "about_label"))) + gtk_label_set_text(GTK_LABEL(set_wgt), about_text); + + /* show near mouse pointer */ + gtk_window_set_position(GTK_WINDOW(about_win), GTK_WIN_POS_MOUSE); + gtk_widget_show(about_win); + } + else + gdk_window_raise(about_win->window); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/configure.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,33 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef _CONFIGURE_H_ +#define _CONFIGURE_H_ + +#include "crossfade.h" + +/* xmms callback prototypes */ +void xfade_about(); +void xfade_configure(); + +#endif /* _CONFIGURE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/convert.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,152 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +/* + * Convert to standard (16bit-le stereo) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include "convert.h" + +void +convert_init(convert_context_t * cc) +{ + memset(cc, 0, sizeof(*cc)); +} + +gint +convert_flow(convert_context_t * cc, gpointer * buffer, gint length, format_t * format) +{ + gpointer data; + gint size; + gint sample_size, sample_count; + gint element_count; + gint16 *out, s; + + if (!cc) + return 0; + if (length <= 0) + return 0; + + /* calculate sample count */ + sample_size = format->is_8bit ? 1 : 2; + sample_count = length / sample_size; + if (sample_count == 0) + return 0; + + /* calculate buffer size */ + size = sample_count * 2; + if (format->nch == 1) + size *= 2; + + /* resize buffer if necessary */ + if (!cc->data || (size > cc->size)) + { + if (!(data = g_realloc(cc->data, size))) + { + DEBUG(("[crossfade] convert: g_realloc(%d) failed!\n", size)); + return 0; + } + cc->data = data; + cc->size = size; + } + + /* calculate number of stereo samples */ + element_count = sample_count; + if (format->nch == 2) + element_count /= 2; + +#define CONVERT(x) \ + if (format->nch == 1) { \ + while (sample_count--) { s = x; *out++ = s; *out++ = s; } \ + } \ + else { \ + while (sample_count--) *out++ = x; \ + } + + out = cc->data; + if (format->is_8bit) + { + if (format->is_unsigned) + { + guint8 *in = *buffer; + CONVERT((gint16) (*in++ ^ 128) << 8); + } + else + { + gint8 *in = *buffer; + CONVERT((gint16) * in++ << 8); + } + } + else + { + if (format->is_unsigned) + { + guint16 *in = *buffer; + if (format->is_swapped) + { + CONVERT((gint16) (((*in & 0x00ff) << 8) | (*in >> 8)) ^ 32768); + in++; + } + else + { + CONVERT((gint16) * in++ ^ 32768); + } + } + else + { + gint16 *in = *buffer; + if (format->is_swapped) + { + CONVERT(((*in & 0x00ff) << 8) | (*in >> 8)); + in++; + } + else + { + if (format->nch == 1) + { + CONVERT(*in++); + } + else + memcpy(out, in, size); + } + } + } + *buffer = cc->data; + + return size; +} + +void +convert_free(convert_context_t * cc) +{ + if (cc->data) + { + g_free(cc->data); + cc->data = NULL; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/convert.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,45 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +/* + * Convert to standard (44100/16bit-le/stereo) + */ + +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +#include "crossfade.h" +#include "format.h" + +typedef struct +{ + gpointer data; + gint size; +} +convert_context_t; + +void convert_init(convert_context_t * cc); +gint convert_flow(convert_context_t * cc, gpointer * buffer, gint length, format_t * format); +void convert_free(convert_context_t * cc); + +#endif /* _CONVERT_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/crossfade.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,2548 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ +/* indent -i8 -ts8 -hnl -bli0 -l128 -npcs -cli8 */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "crossfade.h" +#include "cfgutil.h" +#include "format.h" +#include "convert.h" +#include "timing.h" + +#include "configure.h" +#include "monitor.h" + +#include "interface-2.0.h" +#include "support-2.0.h" + +#ifdef HAVE_LIBFFTW +# include "fft.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#ifdef HAVE_DLFCN_H +# include <dlfcn.h> +#endif +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#undef DEBUG_HARDCORE + +/* output plugin callback prototypes */ +static void xfade_init(); +static void xfade_cleanup(); /* audacious and patched only */ +static void xfade_set_volume(int l, int r); +static void xfade_get_volume(int *l, int *r); +static gint xfade_open_audio(AFormat fmt, int rate, int nch); +static void xfade_write_audio(void *ptr, int length); +static void xfade_close_audio(); +static void xfade_flush(int time); +static void xfade_pause(short paused); +static gint xfade_buffer_free(); +static gint xfade_buffer_playing(); +static gint xfade_written_time(); +static gint xfade_output_time(); + +/* output plugin callback table (extended, needs patched player) */ +static struct +{ + OutputPlugin xfade_op; + void (*cleanup) (void); +} +xfade_op_private = +{ + { + .description = "Crossfade Plugin", + .init = xfade_init, + .cleanup = xfade_cleanup, + .get_volume = xfade_get_volume, + .set_volume = xfade_set_volume, + .open_audio = xfade_open_audio, + .write_audio = xfade_write_audio, + .close_audio = xfade_close_audio, + .flush = xfade_flush, + .pause = xfade_pause, + .buffer_free = xfade_buffer_free, + .buffer_playing = xfade_buffer_playing, + .output_time = xfade_output_time, + .written_time = xfade_written_time, + .about = xfade_about, + .configure = xfade_configure, + .probe_priority = 2, + }, + NULL +}; + +static OutputPlugin *xfade_op = &xfade_op_private.xfade_op; + +static OutputPlugin *xfade_oplist[] = { &xfade_op_private.xfade_op, NULL }; +DECLARE_PLUGIN(crossfade, NULL, NULL, NULL, xfade_oplist, NULL, NULL, NULL); + +/* internal prototypes */ +static void load_symbols(); +static void output_list_hack(); +static gint open_output(); +static void buffer_reset(buffer_t *buf, config_t *cfg); +static void *buffer_thread_f(void *arg); +static void sync_output(); + +/* special XMMS symbols (dynamically looked up, see xfade_init) */ +static gboolean *xmms_playlist_get_info_going = NULL; /* XMMS */ +static gboolean *xmms_is_quitting = NULL; /* XMMS */ +static gboolean *input_stopped_for_restart = NULL; /* XMMS */ +static char * (*playlist_get_fadeinfo)(int) = NULL; /* XMMS patch */ + +static void (*xmms_input_get_song_info)(gchar *, gchar **, gint *); /* XMMS */ +static gchar **xmms_gentitle_format = NULL; /* XMMS private cfg */ + +/* This function has been stolen from libxmms/util.c. */ +void xfade_usleep(gint usec) +{ +#if defined(HAVE_G_USLEEP) + g_usleep(usec); +#elif defined(HAVE_NANOSLEEP) + struct timespec req; + + req.tv_sec = usec / 1000000; + usec -= req.tv_sec * 1000000; + req.tv_nsec = usec * 1000; + + nanosleep(&req, NULL); +#else + struct timeval tv; + + tv.tv_sec = usec / 1000000; + usec -= tv.tv_sec * 1000000; + tv.tv_usec = usec; + select(0, NULL, NULL, NULL, &tv); +#endif +} + + +/* local variables */ +static gboolean realtime; +static gboolean is_http; + +static gint64 streampos; /* position within current song (input bps) */ +static gboolean playing; +gboolean opened; /* TRUE between open_audio() and close_audio() */ +static gboolean paused; /* TRUE: no playback (but still filling buffer) */ +static gboolean stopped; /* TRUE: stop buffer thread ASAP */ +static gboolean eop; /* TRUE: wait until buffer is empty then sync() */ + +#ifdef HAVE_OSS /* avoid 'defined but not used' compiler warning */ +static plugin_config_t default_op_config = DEFAULT_OP_CONFIG; +#endif +static plugin_config_t the_op_config = DEFAULT_OP_CONFIG; + OutputPlugin *the_op = NULL; + gint the_rate = 44100; + +static gboolean input_playing = FALSE; + + gboolean output_opened = FALSE; + gboolean output_restart = FALSE; /* used by XMMS 'songchange' patch */ +static gint output_flush_time = 0; + gint output_offset = 0; +static gint64 output_written = 0; + gint64 output_streampos = 0; + +static gchar zero_4k[4096]; + +#ifdef TIMING_COMMENTS +typedef struct +{ + gboolean enable; + gint len_ms, volume, skip_ms, ofs_ms; +} +timing_half_config_t; + +typedef struct +{ + timing_half_config_t in; + timing_half_config_t out; +} +timing_config_t; + +static timing_config_t last_timing, current_timing; +#endif + +/* + * Available fade configs: + * + * fc_start: First song, only in_len and in_level are used + * fc_xfade: Automatic crossfade at end of song + * fc_album: Like xfade but for consecutive songs of the same album + * fc_manual: Manual crossfade (triggered by Next or Prev) + * fc_stop: Last song, only out_len and out_level are used + * fc_eop: Last song, only out_len and out_level are used + * + * NOTE: As of version 0.2 of xmms-crossfade, + * only xfade and manual are implemented. + * + * With version 0.2.3, fc_album has been added. + * + * With 0.2.4, all configs are implemented: + * + * Available parameters: + * + * | start | xfade | manual | album | stop | eop + * ------------+-------+-------+--------+-------+------+------ + * in_len | yes | yes | yes | no | no | no + * in_volume | yes | yes | yes | no | no | no + * offset | no | yes | yes | no | +yes | +yes + * out_len | no | yes | yes | no | yes | yes + * out_volume | no | yes | yes | no | yes | yes + * flush (*) | no | no | yes | no | yes | no + * ------------+-------+-------+--------+-------+------+------ + * + * Parameters marked with (*) are not configureable by the user + * + * The offset parameters for 'stop' and 'eop' is used to store the + * length of the additional silence to be added. It may be >= 0 only. + * + */ + +static struct timeval last_close; +static struct timeval last_write; + +static gchar *last_filename = NULL; + +static format_t in_format; +static format_t out_format; + +static buffer_t the_buffer; + buffer_t *buffer = &the_buffer; + +static THREAD buffer_thread; + MUTEX buffer_mutex = MUTEX_INITIALIZER; + +static convert_context_t convert_context; +#ifdef HAVE_LIBFFTW +static fft_context_t fft_context; +#endif + +static config_t the_config; + config_t *config = &the_config; + config_t config_default = CONFIG_DEFAULT; + +static fade_config_t *fade_config = NULL; + + +/* this is the entry point for XMMS */ +OutputPlugin * +get_oplugin_info() +{ + return xfade_op; +} + +OutputPlugin * +get_crossfade_oplugin_info() +{ + return xfade_op; +} + +static gboolean +open_output_f(gpointer data) +{ + DEBUG(("[crossfade] open_output_f: pid=%d\n", getpid())); + open_output(); + return FALSE; /* FALSE = 'do not call me again' */ +} + +void +xfade_realize_config() /* also called by xfade_init() */ +{ + /* 0.3.0: keep device opened */ + if (config->output_keep_opened && !output_opened) + { + DEBUG(("[crossfade] realize_config: keeping output opened...\n")); + + /* 0.3.1: HACK: this will make sure that we start playing silence after startup */ + gettimeofday(&last_close, NULL); + + /* 0.3.1: HACK: Somehow, if we open output here at XMMS startup, there + will be leftover filedescriptors later when closing output again. + Opening output in a timeout function seems to work around this... */ + DEBUG(("[crossfade] realize_config: adding timeout (pid=%d)\n", (int) getpid())); + g_timeout_add(0, open_output_f, NULL); + } +} + +static gint +output_list_f(gconstpointer a, gconstpointer b) +{ + OutputPlugin *op = (OutputPlugin *) a; + gchar *name = (gchar *) b; + + return strcmp(g_basename(op->filename), name); +} + +static OutputPlugin * +find_output() +{ + GList *list, *element; + OutputPlugin *op = NULL; + + /* find output plugin */ + { + if (config->op_name && (list = xfplayer_get_output_list())) + if ((element = g_list_find_custom(list, config->op_name, output_list_f))) + op = element->data; + + if (op == xfade_op) + { + DEBUG(("[crossfade] find_output: can't use myself as output plugin!\n")); + op = NULL; + } + else if (!op) + { + DEBUG(("[crossfade] find_output: could not find output plugin \"%s\"\n", + config->op_name ? config->op_name : "#NULL#")); + } + else /* ok, we have a plugin. last, get its compatibility options */ + xfade_load_plugin_config(config->op_config_string, config->op_name, &the_op_config); + } + + return op; +} + +static gint +open_output() +{ + /* sanity check */ + if (output_opened) + DEBUG(("[crossfade] open_output: WARNING: output_opened=TRUE!\n")); + + /* reset output_* */ + output_opened = FALSE; + output_flush_time = 0; + output_offset = 0; + output_written = 0; + output_streampos = 0; + + /* get output plugin (this will also init the_op_config) */ + if (!(the_op = find_output())) + { + DEBUG(("[crossfade] open_output: could not find any output!\n")); + return -1; + } + + /* print output plugin info */ + DEBUG(("[crossfade] open_output: using \"%s\" for output", the_op->description ? the_op->description : "#NULL#")); + + if (realtime) + DEBUG((" (RT)")); + + if (the_op_config.throttle_enable) + DEBUG((realtime ? " (throttled (disabled with RT))" : " (throttled)")); + + if (the_op_config.max_write_enable) + DEBUG((" (max_write=%d)", the_op_config.max_write_len)); + + DEBUG(("\n")); + + /* setup sample rate (note that OUTPUT_RATE is #defined as the_rate) */ + the_rate = config->output_rate; + + /* setup out_format. use host byte order for easy math */ + setup_format(FMT_S16_NE, OUTPUT_RATE, OUTPUT_NCH, &out_format); + + /* open plugin */ + if (!the_op->open_audio(out_format.fmt, out_format.rate, out_format.nch)) + { + DEBUG(("[crossfade] open_output: open_audio() failed!\n")); + the_op = NULL; + return -1; + } + + /* clear buffer struct */ + memset(buffer, 0, sizeof(*buffer)); + + /* calculate buffer size */ + buffer->mix_size = MS2B(xfade_mix_size_ms(config)) & -4; + buffer->sync_size = MS2B(config->sync_size_ms) & -4; + buffer->preload_size = MS2B(config->preload_size_ms) & -4; + + buffer->size = (buffer->mix_size + /* mixing area */ + buffer->sync_size + /* additional sync */ + buffer->preload_size); /* preload */ + + DEBUG(("[crossfade] open_output: buffer: size=%d (%d+%d+%d=%d ms) (%d Hz)\n", + buffer->size, + B2MS(buffer->mix_size), + B2MS(buffer->preload_size), + B2MS(buffer->sync_size), + B2MS(buffer->size), + the_rate)); + + /* allocate buffer */ + if (!(buffer->data = g_malloc0(buffer->size))) + { + DEBUG(("[crossfade] open_output: error allocating buffer!\n")); + the_op->close_audio(); + the_op = NULL; + return -1; + } + + /* reset buffer */ + buffer_reset(buffer, config); + + /* make sure stopped is TRUE -- otherwise the buffer thread would + * stop again immediatelly after it has been started. */ + stopped = FALSE; + + /* create and run buffer thread */ + if (THREAD_CREATE(buffer_thread, buffer_thread_f)) + { + PERROR("[crossfade] open_output: thread_create()"); + g_free(buffer->data); + the_op->close_audio(); + the_op = NULL; + return -1; + } + SCHED_YIELD; + + /* start updating monitor */ + xfade_start_monitor(); + + /* done */ + output_opened = TRUE; + return 0; +} + +static void +xfade_init() +{ + /* load config */ + memset(config, 0, sizeof(*config)); + *config = config_default; + xfade_load_config(); + + /* set default strings if there is no existing config */ + if (!config->oss_alt_audio_device) config->oss_alt_audio_device = g_strdup(DEFAULT_OSS_ALT_AUDIO_DEVICE); + if (!config->oss_alt_mixer_device) config->oss_alt_mixer_device = g_strdup(DEFAULT_OSS_ALT_MIXER_DEVICE); + if (!config->op_config_string) config->op_config_string = g_strdup(DEFAULT_OP_CONFIG_STRING); + if (!config->op_name) config->op_name = g_strdup(DEFAULT_OP_NAME); + + /* check for realtime priority, it needs some special attention */ + realtime = xfplayer_check_realtime_priority(); + + /* show monitor win if enabled in config */ + xfade_check_monitor_win(); + + /* init contexts */ + convert_init(&convert_context); +#ifdef HAVE_LIBFFTW + fft_init(&fft_context); +#endif + + /* reset */ + stopped = FALSE; + + /* find current output plugin early so that volume control works + * even if playback has not started yet. */ + if (!(the_op = find_output())) + DEBUG(("[crossfade] init: could not find any output!\n")); + + /* load any dynamic linked symbols */ + load_symbols(); + + /* HACK: make sure we are at the beginning of XMMS' output plugin list */ + output_list_hack(); + + /* realize config -- will also setup the pre-mixing effect plugin */ + xfade_realize_config(); +} + +static void +load_symbols() +{ +#ifdef HAVE_DLFCN_H + void *handle; + char *error; + gchar **xmms_cfg; + gchar * (*get_gentitle_format)(); + + /* open ourselves (that is, the XMMS binary) */ + handle = dlopen(NULL, RTLD_NOW); + if (!handle) + { + DEBUG(("[crossfade] init: dlopen(NULL) failed!\n")); + return; + } + + /* check for XMMS patches */ + DEBUG(("[crossfade] load_symbols: input_stopped_for_restart:")); + input_stopped_for_restart = dlsym(handle, "input_stopped_for_restart"); + DEBUG((!(error = dlerror())? " found\n" : " missing\n")); + + DEBUG(("[crossfade] load_symbols: is_quitting:")); + xmms_is_quitting = dlsym(handle, "is_quitting"); + DEBUG((!(error = dlerror())? " found\n" : " missing\n")); + + DEBUG(("[crossfade] load_symbols: playlist_get_fadeinfo:")); + playlist_get_fadeinfo = dlsym(handle, "playlist_get_fadeinfo"); + DEBUG((!(error = dlerror())? " found\n" : " missing\n")); + + /* check for some XMMS functions */ + xmms_playlist_get_info_going = dlsym(handle, "playlist_get_info_going"); + xmms_input_get_song_info = dlsym(handle, "input_get_song_info"); + + /* HACK: direct access to XMMS' config 'gentitle_format' */ + xmms_cfg = dlsym(handle, "cfg"); + get_gentitle_format = dlsym(handle, "xmms_get_gentitle_format"); + if (xmms_cfg && get_gentitle_format) + { + gchar *format = get_gentitle_format(); + + int i = 128; + gchar **p = (gchar **)xmms_cfg; + for (i = 128; i > 0 && *p != format; i--, p++); + if (*p == format) + xmms_gentitle_format = p; + } + + dlclose(handle); +#endif +} + +/* + HACK: Try to move ourselves to the beginning of XMMS output plugin list, + so that we will be freed first when XMMS is quitting. This way, we + avoid the segfault when using ALSA as the output plugin. +*/ +static void +output_list_hack() +{ + GList *output_list = xfplayer_get_output_list(); + if (!output_list) + return; + + int i0 = g_list_index(output_list, xfade_op), i1; + + GList *first = g_list_first(output_list); + GList *xfade = g_list_find(output_list, xfade_op); + xfade->data = first->data; + first->data = xfade_op; + + i1 = g_list_index(output_list, xfade_op); + if (i0 != i1) + DEBUG(("[crossfade] output_list_hack: crossfade moved from index %d to %d\n", i0, i1)); +} + +void +xfade_get_volume(int *l, int *r) +{ + if (config->mixer_software) + { + *l = config->mixer_reverse + ? config->mixer_vol_right + : config->mixer_vol_left; + *r = config->mixer_reverse + ? config->mixer_vol_left + : config->mixer_vol_right; + } + else + { + if (the_op && the_op->get_volume) + { + if (config->mixer_reverse) + the_op->get_volume(r, l); + else + the_op->get_volume(l, r); + } + } + + /* DEBUG(("[crossfade] xfade_get_volume: l=%d r=%d\n", *l, *r)); */ +} + +void +xfade_set_volume(int l, int r) +{ + /* DEBUG(("[crossfade] xfade_set_volume: l=%d r=%d\n", l, r)); */ + + if (!config->enable_mixer) + return; + + if (the_op && the_op->set_volume) + { + if (config->mixer_reverse) + the_op->set_volume(r, l); + else + the_op->set_volume(l, r); + } +} + +/*** buffer stuff ***********************************************************/ + +static void +buffer_mfg_reset(buffer_t *buf, config_t *cfg) +{ + buf->mix = 0; + buf->fade = 0; + buf->gap = (cfg->gap_lead_enable ? MS2B(cfg->gap_lead_len_ms) & -4 : 0); + buf->gap_len = buf->gap; + buf->gap_level = cfg->gap_lead_level; + buf->gap_killed = 0; + buf->skip = 0; + buf->skip_len = 0; +} + +static void +buffer_reset(buffer_t *buf, config_t *cfg) +{ + buffer_mfg_reset(buf, cfg); + + buf->rd_index = 0; + buf->used = 0; + buf->preload = buf->preload_size; + + buf->silence = 0; + buf->silence_len = 0; + buf->reopen = -1; + buf->pause = -1; +} + +/****************************************************************************/ + +static void +xfade_apply_fade_config(fade_config_t *fc) +{ + gint out_skip, in_skip; + gint avail, out_len, in_len, offset, preload; + gint index, length, fade, n; + gfloat out_scale, in_scale; + gboolean out_skip_clipped = FALSE; + gboolean out_len_clipped = FALSE; + gboolean offset_clipped = FALSE; + + /* Overwrites mix and fade; may add silence */ + + /* + * Example 1: offset < 0 --> mix streams together + * Example 2: offset > 0 --> insert pause between streams + * + * |----- out_len -----| * |out_len| + * | | * | | + * ~~~~~-_ /T~~~~~~~T~~ * ~~~~~\ | /T~~ + * ~-_ / | | * \ | / | + * ~-_/ | | * \ | / | + * /~-_| | * \ | / | + * / T-_ | * \ | / | + * / | ~-_ | * \ | / | + * _________/______|_____~-|__ * ___________\__________/______|__ + * |in_len| | * | |in_len| + * |<-- offset ---| * |offset-->| + * + * a) avail: max(0, used - preload) + * b) out_len: 0 .. avail + * c) in_len: 0 .. # + * d) offset: -avail .. buffer->mix_size - out_size + * e) skip: min(used, preload) + * + */ + + out_scale = 1.0f - (gfloat) xfade_cfg_fadeout_volume(fc) / 100.0f; + in_scale = 1.0f - (gfloat) xfade_cfg_fadein_volume (fc) / 100.0f; + + /* rules (see above) */ + /* a: leave preload untouched */ + avail = buffer->used - buffer->preload_size; + if (avail < 0) + avail = 0; + + /* skip end of song */ + out_skip = MS2B(xfade_cfg_out_skip(fc)) & -4; + if (out_skip > avail) + { + DEBUG(("[crossfade] apply_fade_config: WARNING: clipping out_skip (%d -> %d)!\n", B2MS(out_skip), B2MS(avail))); + out_skip = avail; + out_skip_clipped = TRUE; + } + + if (out_skip > 0) + { + buffer->used -= out_skip; + avail -= out_skip; + } + + /* b: fadeout */ + out_len = MS2B(xfade_cfg_fadeout_len(fc)) & -4; + if (out_len > avail) + { + DEBUG(("[crossfade] apply_fade_config: WARNING: clipping out_len (%d -> %d)!\n", B2MS(out_len), B2MS(avail))); + out_len = avail; + out_len_clipped = TRUE; + } + else if (out_len < 0) + out_len = 0; + + /* skip beginning of song */ + in_skip = MS2B(xfade_cfg_in_skip(fc)) & -4; + if (in_skip < 0) + in_skip = 0; + + /* c: fadein */ + in_len = MS2B(xfade_cfg_fadein_len(fc)) & -4; + if (in_len < 0) + in_len = 0; + + /* d: offset (mixing point) */ + offset = MS2B(xfade_cfg_offset(fc)) & -4; + if (offset < -avail) + { + DEBUG(("[crossfade] apply_fade_config: WARNING: clipping offset (%d -> %d)!\n", B2MS(offset), -B2MS(avail))); + offset = -avail; + offset_clipped = TRUE; + } + if (offset > (buffer->mix_size - out_len)) + offset = buffer->mix_size - out_len; + + /* e */ + preload = buffer->preload_size; + if (preload > buffer->used) + preload = buffer->used; + + /* cut off rest of stream (decreases latency on manual songchange) */ + if (fc->flush) + { + gint cutoff = avail - MAX(out_len, -offset); /* MAX() -> glib.h */ + if (cutoff > 0) + { + DEBUG(("[crossfade] apply_fade_config: %d ms flushed\n", B2MS(cutoff))); + buffer->used -= cutoff; + avail -= cutoff; + } + + /* make sure there is no pending silence */ + buffer->silence = 0; + buffer->silence_len = 0; + } + + /* begin modifying buffer at index */ + index = (buffer->rd_index + buffer->used - out_len) % buffer->size; + + /* fade out (modifies buffer directly) */ + fade = 0; + length = out_len; + while (length > 0) + { + gint16 *p = buffer->data + index; + gint blen = buffer->size - index; + if (blen > length) blen = length; + + for (n = blen / 4; n > 0; n--) + { + gfloat factor = 1.0f - (((gfloat) fade / out_len) * out_scale); + *p = (gfloat)*p * factor; p++; + *p = (gfloat)*p * factor; p++; + fade += 4; + } + + index = (index + blen) % buffer->size; + length -= blen; + } + + /* Initialize fadein. Note that the actual fading / mixing will be done + * on-the-fly when audio data is received by xfade_write_audio() */ + + /* start skipping */ + if (in_skip > 0) + { + buffer->skip = in_skip; + buffer->skip_len = in_skip; + } + else + buffer->skip = 0; + + /* start fading in */ + if (in_len > 0) + { + buffer->fade = in_len; + buffer->fade_len = in_len; + buffer->fade_scale = in_scale; + } + else + buffer->fade = 0; + + /* start mixing */ + if (offset < 0) + { + length = -offset; + buffer->mix = length; + buffer->used -= length; + } + else + buffer->mix = 0; + + /* start silence if applicable (will be applied in buffer_thread_f) */ + if (offset > 0) + { + if ((buffer->silence > 0) || (buffer->silence_len > 0)) + DEBUG(("[crossfade] apply_config: WARNING: silence in progress (%d/%d ms)\n", + B2MS(buffer->silence), B2MS(buffer->silence_len))); + + buffer->silence = buffer->used; + buffer->silence_len = offset; + } + + /* done */ + if (in_skip || out_skip) + DEBUG(("[crossfade] apply_fade_config: out_skip=%d in_skip=%d\n", B2MS(out_skip), B2MS(in_skip))); + DEBUG(("[crossfade] apply_fade_config: avail=%d out=%d in=%d offset=%d preload=%d\n", + B2MS(avail), B2MS(out_len), B2MS(in_len), B2MS(offset), B2MS(preload))); +} + +static gint +extract_track(const gchar *name) +{ +#if 1 + /* skip non-digits at beginning */ + while (*name && !isdigit(*name)) + name++; + + return atoi(name); +#else + /* Remove all but numbers. + * Will not work if a filename has number in the title, like "track-03-U2.mp3" + * Ideally, should look into id3 track entry and fallback to filename + * */ + gchar temp[8]; + int t = 0; + + memset(temp, 0, sizeof(temp)); + while (*name != '\0' && t < sizeof(temp)) + { + if (strcmp(name, "mp3") == 0) + break; + + if (isdigit(*name)) + temp[t++] = *name; + + name++; + } + return atoi(temp); +#endif +} + +static gint +album_match(gchar *old, gchar *new) +{ + gchar *old_dir, *new_dir; + gboolean same_dir; + gint old_track = 0, new_track = 0; + + if (!old || !new) + return 0; + + old_dir = g_dirname(old); + new_dir = g_dirname(new); + same_dir = !strcmp(old_dir, new_dir); + g_free(old_dir); + g_free(new_dir); + + if (!same_dir) + { + DEBUG(("[crossfade] album_match: no match (different dirs)\n")); + return 0; + } + + old_track = extract_track(g_basename(old)); + new_track = extract_track(g_basename(new)); + + if (new_track <= 0) + { + DEBUG(("[crossfade] album_match: can't parse track number:\n")); + DEBUG(("[crossfade] album_match: ... \"%s\"\n", g_basename(new))); + return 0; + } + + if ((old_track < 0) || (old_track + 1 != new_track)) + { + DEBUG(("[crossfade] album_match: no match (same dir, but non-successive (%d, %d))\n", old_track, new_track)); + return 0; + } + + DEBUG(("[crossfade] album_match: match detected (same dir, successive tracks (%d, %d))\n", old_track, new_track)); + + return old_track; +} + +static gint +xfade_open_audio(AFormat fmt, int rate, int nch) +{ + gint pos; + gchar *file, *title, *comment; +#if defined(HAVE_ID3LIB) + id3_t id3; +#endif + + struct timeval tv; + glong dt; + + DEBUG(("[crossfade]\n")); + DEBUG(("[crossfade] open_audio: pid=%d\n", (int) getpid())); + + /* sanity... don't do anything about it */ + if (opened) + DEBUG(("[crossfade] open_audio: WARNING: already opened!\n")); + + /* get filename */ + pos = xfplaylist_get_position (); + file = xfplaylist_get_filename (pos); + title = xfplaylist_get_songtitle(pos); + comment = playlist_get_fadeinfo ? playlist_get_fadeinfo(pos) : NULL; + + if (!file) + file = g_strdup(title); + + DEBUG(("[crossfade] open_audio: bname=\"%s\"\n", g_basename(file))); + DEBUG(("[crossfade] open_audio: title=\"%s\"\n", title)); + +#if 0 + /* HACK: try to get comment and track number from xmms by sneaking in a custom title format */ + if (xmms_gentitle_format) + { + gchar *old_gentitle_format = *xmms_gentitle_format; + + gchar *temp_title = NULL; + gint temp_length = 0; + + xmms_input_get_song_info(file, &temp_title, &temp_length); + DEBUG(("[crossfade] open_audio: TITLE: %s\n", temp_title)); + g_free(temp_title); + + *xmms_gentitle_format = "%n/%c"; + + xmms_input_get_song_info(file, &temp_title, &temp_length); + DEBUG(("[crossfade] open_audio: TRACK/COMMENT: %s\n", temp_title)); + g_free(temp_title); + + *xmms_gentitle_format = old_gentitle_format; + } +#endif + + /* try to read comment from ID3 tag */ +#if defined(HAVE_ID3LIB) + if (!comment && get_id3(file, &id3)) + comment = g_strdup(id3.comment); +#endif + + if (comment) + DEBUG(("[crossfade] open_audio: comment=\"%s\"\n", comment)) + else + DEBUG(("[crossfade] open_audio: comment=NULL\n")); + + /* is this an automatic crossfade? */ + if (last_filename && (fade_config == &config->fc[FADE_CONFIG_XFADE])) + { + /* check if next song is the same as the current one */ + if (config->no_xfade_if_same_file && !strcmp(last_filename, file)) + { + DEBUG(("[crossfade] open_audio: same file, disabling crossfade\n")); + fade_config = &config->fc[FADE_CONFIG_ALBUM]; + } + + /* check if next song is the next song from the same album */ + else if (config->album_detection && album_match(last_filename, file)) + { + gboolean use_fc_album = FALSE; + + if (xfade_cfg_gap_trail_enable(config)) + { + DEBUG(("[crossfade] album_match: " + "trailing gap: length=%d/%d ms\n", B2MS(buffer->gap_killed), B2MS(buffer->gap_len))); + + if (buffer->gap_killed < buffer->gap_len) + { + DEBUG(("[crossfade] album_match: " + "trailing gap: -> no silence, probably pre-faded\n")); + use_fc_album = TRUE; + } + else + { + DEBUG(("[crossfade] album_match: " "trailing gap: -> silence, sticking to XFADE\n")); + } + } + else + { + DEBUG(("[crossfade] album_match: " "trailing gap killer disabled\n")); + use_fc_album = TRUE; + } + + if (use_fc_album) + { + DEBUG(("[crossfade] album_match: " "-> using FADE_CONFIG_ALBUM\n")); + fade_config = &config->fc[FADE_CONFIG_ALBUM]; + } + } + } + g_free(last_filename); + last_filename = g_strdup(file); + +#if 0 + /* FIXME: finish this */ + /* Check if this is a short song. */ + if (fade_config == &config->fc[FADE_CONFIG_XFADE]) + { + DEBUG(("*** XFADE:\n")); + int current_length = playlist_get_current_length(); + DEBUG(("*** length=%d\n", current_length)); + if (current_length < 30 * 1000) + fade_config = &config->fc[FADE_CONFIG_ALBUM]; + } +#endif + +#ifdef TIMING_COMMENTS + last_timing = current_timing; + current_timing. in.enable = FALSE; + current_timing.out.enable = FALSE; + if (comment) + { + gchar *str; + if ((str = strstr(comment, "fadein="))) + { + current_timing.in.enable = + (3 == sscanf(str + 7, "%d,%d,%d", + ¤t_timing.in.len_ms, + ¤t_timing.in.volume, + ¤t_timing.in.skip_ms)); + current_timing.in.ofs_ms = 0; /* not used */ + } + + if ((str = strstr(comment, "fadeout="))) + { + current_timing.out.enable = + (4 == sscanf(str + 8, "%d,%d,%d,%d", + ¤t_timing.out.len_ms, + ¤t_timing.out.volume, + ¤t_timing.out.skip_ms, + ¤t_timing.out.ofs_ms)); + } + } + +#if 0 + // + // use the fade info only on a regular, non-manual songchange + // + if (fade_config && !((fade_config->config == FADE_CONFIG_XFADE) || + (fade_config->config == FADE_CONFIG_ALBUM))) + last_timing.out.enable = FALSE; +#endif + +#if 1 + if (last_timing.out.enable || current_timing.in.enable) +#else + if ((last_timing.out.enable || current_timing.in.enable) && + (!fade_config || + (fade_config->config == FADE_CONFIG_XFADE) || + (fade_config->config == FADE_CONFIG_ALBUM))) +#endif + { + config->fc[FADE_CONFIG_TIMING].out_len_ms = xfade_cfg_fadeout_len (fade_config); + config->fc[FADE_CONFIG_TIMING].out_volume = xfade_cfg_fadeout_volume(fade_config); + config->fc[FADE_CONFIG_TIMING].out_skip_ms = 0; + config->fc[FADE_CONFIG_TIMING].ofs_custom_ms = xfade_cfg_offset (fade_config); + config->fc[FADE_CONFIG_TIMING].in_skip_ms = 0; + config->fc[FADE_CONFIG_TIMING].in_len_ms = xfade_cfg_fadein_len (fade_config); + config->fc[FADE_CONFIG_TIMING].in_volume = xfade_cfg_fadein_volume (fade_config); + config->fc[FADE_CONFIG_TIMING].flush = fade_config && fade_config->flush; + + if (last_timing.out.enable && current_timing.in.enable) + config->fc[FADE_CONFIG_TIMING].ofs_custom_ms = 0; + + if (last_timing.out.enable) + { + DEBUG(("[crossfade] open_audio: TIMING: out: enable=%d len=%d volume=%d skip=%d ofs=%d\n", + last_timing.out.enable, + last_timing.out.len_ms, + last_timing.out.volume, + last_timing.out.skip_ms, + last_timing.out.ofs_ms)); + + config->fc[FADE_CONFIG_TIMING].out_len_ms = last_timing.out.len_ms; + config->fc[FADE_CONFIG_TIMING].out_volume = last_timing.out.volume; + config->fc[FADE_CONFIG_TIMING].out_skip_ms = last_timing.out.skip_ms; + config->fc[FADE_CONFIG_TIMING].ofs_custom_ms += last_timing.out.ofs_ms; + } + if (current_timing.in.enable) + { + DEBUG(("[crossfade] open_audio: TIMING: in: enable=%d len=%d volume=%d skip=%d\n", + current_timing.in.enable, + current_timing.in.len_ms, + current_timing.in.volume, + current_timing.in.skip_ms)); + + config->fc[FADE_CONFIG_TIMING].in_skip_ms = current_timing.in.skip_ms; + config->fc[FADE_CONFIG_TIMING].in_len_ms = current_timing.in.len_ms; + config->fc[FADE_CONFIG_TIMING].in_volume = current_timing.in.volume; + config->fc[FADE_CONFIG_TIMING].ofs_custom_ms -= current_timing.in.ofs_ms; + /* NOTE: in.ofs_ms is currently not used always 0 */ + } + + fade_config = &config->fc[FADE_CONFIG_TIMING]; + } +#endif + + /* cleanup */ + g_free(file); file = NULL; + g_free(title); title = NULL; + g_free(comment); comment = NULL; + + /* check for HTTP streaming */ + if (config->enable_http_workaround && (0 == strncasecmp(file, "http://", 7))) + { + DEBUG(("[crossfade] open_audio: HTTP underrun workaround enabled.\n")); + is_http = TRUE; + } + else + is_http = FALSE; + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + /* reset writer timeout */ + gettimeofday(&last_write, NULL); + + /* calculate time since last close() (don't care about overflows at 24h) */ + if (output_opened) + { + gettimeofday(&tv, NULL); + dt = (tv.tv_sec - last_close.tv_sec) * 1000 + (tv.tv_usec - last_close.tv_usec) / 1000; + } + else + dt = 0; + + DEBUG(("[crossfade] open_audio: fmt=%s rate=%d nch=%d dt=%ld ms\n", format_name(fmt), rate, nch, dt)); + + /* check format */ + if (setup_format(fmt, rate, nch, &in_format) < 0) + { + DEBUG(("[crossfade] open_audio: format not supported!\n")); + return 0; + } + + /* (re)open the device if necessary */ + if (!output_opened) + { + if (open_output()) + { + DEBUG(("[crossfade] open_audio: error opening/configuring output!\n")); + MUTEX_UNLOCK(&buffer_mutex); + return 0; + } + fade_config = &config->fc[FADE_CONFIG_START]; + } + + /* reset */ + streampos = 0; + playing = TRUE; + opened = TRUE; + paused = FALSE; + + /* reset mix/fade/gap */ + buffer_mfg_reset(buffer, config); + + /* enable gap killer / zero crossing only for automatic/album songchange */ + switch (fade_config->config) + { + case FADE_CONFIG_XFADE: + case FADE_CONFIG_ALBUM: + break; + + default: + buffer->gap = GAP_SKIPPING_DONE; + } + + /* restart realtime throttling */ + output_written = 0; + + /* start mixing */ + switch (fade_config ? fade_config->type : -1) + { + case FADE_TYPE_FLUSH: + DEBUG(("[crossfade] open_audio: FLUSH:\n")); + + /* flush output plugin */ + the_op->flush(0); + output_streampos = 0; + + /* flush buffer */ + buffer_reset(buffer, config); + + /* apply fade config (pause/fadein after flush) */ + xfade_apply_fade_config(fade_config); + + /* also repopen device (if configured so in the plugin compat. options) */ + if (the_op_config.force_reopen) + { + buffer->reopen = 0; + buffer->reopen_sync = FALSE; + } + break; + + case FADE_TYPE_REOPEN: + DEBUG(("[crossfade] open_audio: REOPEN:\n")); + + /* flush buffer if applicable */ + if (fade_config->flush) + buffer_reset(buffer, config); + + if (buffer->reopen >= 0) + DEBUG(("[crossfade] open_audio: REOPEN: WARNING: reopen in progress (%d ms)\n", + B2MS(buffer->reopen))); + + /* start reopen countdown (will be executed in buffer_thread_f) */ + buffer->reopen = buffer->used; /* may be 0 */ + buffer->reopen_sync = FALSE; + break; + + case FADE_TYPE_NONE: + case FADE_TYPE_PAUSE: + case FADE_TYPE_SIMPLE_XF: + case FADE_TYPE_ADVANCED_XF: + case FADE_TYPE_FADEIN: + case FADE_TYPE_FADEOUT: + DEBUG(("[crossfade] open_audio: XFADE:\n")); + + /* apply fade config (do fadeout, init mix/fade/gap, add silence) */ + xfade_apply_fade_config(fade_config); + + /* set reopen countdown. after buffer_thread_f has written + * buffer->reopen bytes, it will close/reopen the output plugin. */ + if (the_op_config.force_reopen && !(fade_config->config == FADE_CONFIG_START)) + { + if (buffer->reopen >= 0) + DEBUG(("[crossfade] open_audio: XFADE: WARNING: reopen in progress (%d ms)\n", + B2MS(buffer->reopen))); + buffer->reopen = buffer->used; + buffer->reopen_sync = TRUE; + } + break; + } + + /* calculate offset of the output plugin */ + output_offset = the_op->written_time() + B2MS(buffer->used) + B2MS(buffer->silence_len); + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + + /* done */ + return 1; +} + +void +xfade_write_audio(void *ptr, int length) +{ + gint free; + gint ofs = 0; + format_t format; + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] write_audio: ptr=0x%08lx, length=%d\n", (long) ptr, length)); +#endif + + /* sanity */ + if (length <= 0) + return; + + if (length & 3) + { + DEBUG(("[crossfade] write_audio: truncating %d bytes!\n", length & 3)); + length &= -4; + } + + /* update input accumulator (using input format size) */ + streampos += length; + + /* convert sample format (signed-16bit-ne 44100hz stereo) */ + format_copy(&format, &in_format); + length = convert_flow(&convert_context, (gpointer *) &ptr, length, &format); + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + /* check if device has been closed, reopen if necessary */ + if (!output_opened) + { + if (open_output()) + { + DEBUG(("[crossfade] write_audio: reopening failed!\n")); + MUTEX_UNLOCK(&buffer_mutex); + return; + } + } + + /* reset timeout */ + gettimeofday(&last_write, NULL); + + /* calculate free buffer space, check for overflow (should never happen :) */ + free = buffer->size - buffer->used; + if (length > free) + { + DEBUG(("[crossfade] write_audio: %d bytes truncated!\n", length - free)); + length = free; + } + + /* skip beginning of song */ + if ((length > 0) && (buffer->skip > 0)) + { + gint blen = MIN(length, buffer->skip); + + buffer->skip -= blen; + length -= blen; + ptr += blen; + } + + /* kill leading gap */ + if ((length > 0) && (buffer->gap > 0)) + { + gint blen = MIN(length, buffer->gap); + gint16 *p = ptr; + gint index = 0; + + gint16 left, right; + while (index < blen) + { + left = *p++, right = *p++; + if (ABS(left) >= buffer->gap_level) break; + if (ABS(right) >= buffer->gap_level) break; + index += 4; + } + + buffer->gap -= index; + length -= index; + ptr += index; + + if ((index < blen) || (buffer->gap <= 0)) + { + buffer->gap_killed = buffer->gap_len - buffer->gap; + buffer->gap = 0; + + DEBUG(("[crossfade] write_audio: leading gap size: %d/%d ms\n", + B2MS(buffer->gap_killed), B2MS(buffer->gap_len))); + + /* fix streampos */ + streampos -= (gint64) buffer->gap_killed * in_format.bps / out_format.bps; + } + } + + /* start skipping to next crossing (if enabled) */ + if (buffer->gap == 0) + { + if (config->gap_crossing) + { + buffer->gap = GAP_SKIPPING_POSITIVE; + buffer->gap_skipped = 0; + } + else + buffer->gap = GAP_SKIPPING_DONE; + } + + /* skip until next zero crossing (pos -> neg) */ + if ((length > 0) && (buffer->gap == GAP_SKIPPING_POSITIVE)) + { + gint16 *p = ptr; + gint index = 0; + + gint16 left; + while (index < length) + { + left = *p++; + p++; + if (left < 0) + break; + index += 4; + } + + buffer->gap_skipped += index; + length -= index; + ptr += index; + + if (index < length) + buffer->gap = GAP_SKIPPING_NEGATIVE; + } + + /* skip until next zero crossing (neg -> pos) */ + if ((length > 0) && (buffer->gap == GAP_SKIPPING_NEGATIVE)) + { + gint16 *p = ptr; + gint index = 0; + + gint16 left; + while (index < length) + { + left = *p++; + p++; + if (left >= 0) + break; + index += 4; + } + + buffer->gap_skipped += index; + length -= index; + ptr += index; + + if (index < length) + { + DEBUG(("[crossfade] write_audio: %d samples to next crossing\n", buffer->gap_skipped)); + buffer->gap = GAP_SKIPPING_DONE; + } + } + + /* update preload. the buffer thread will not write any + * data to the device before preload is decreased below 1. */ + if ((length > 0) && (buffer->preload > 0)) + buffer->preload -= length; + + /* fadein -- FIXME: is modifying the input/effect buffer safe? */ + if ((length > 0) && (buffer->fade > 0)) + { + gint16 *p = ptr; + gint blen = MIN(length, buffer->fade); + gint n; + + for (n = blen / 4; n > 0; n--) + { + gfloat factor = 1.0f - (((gfloat) buffer->fade / buffer->fade_len) * buffer->fade_scale); + *p = (gfloat)*p * factor; p++; + *p = (gfloat)*p * factor; p++; + buffer->fade -= 4; + } + } + + /* mix */ + while ((length > 0) && (buffer->mix > 0)) + { + gint wr_index = (buffer->rd_index + buffer->used) % buffer->size; + gint blen = buffer->size - wr_index; + gint16 *p1 = buffer->data + wr_index; + gint16 *p2 = ptr + ofs; + gint n; + + if (blen > length) blen = length; + if (blen > buffer->mix) blen = buffer->mix; + + for (n = blen / 2; n > 0; n--) + { + gint out = (gint)*p1 + *p2++; /* add */ + if (out > 32767) /* clamp */ + *p1++ = 32767; + else if (out < -32768) + *p1++ = -32768; + else + *p1++ = out; + } + + buffer->used += blen; + buffer->mix -= blen; + length -= blen; + ofs += blen; + } + + /* normal write */ + while (length > 0) + { + gint wr_index = (buffer->rd_index + buffer->used) % buffer->size; + gint blen = buffer->size - wr_index; + + if (blen > length) + blen = length; + + memcpy(buffer->data + wr_index, ptr + ofs, blen); + + buffer->used += blen; + length -= blen; + ofs += blen; + } + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] write_audio: done.\n")); +#endif +} + +/* sync_output: wait for output plugin to finish playback */ +/* is only called from within buffer_thread_f */ +static void +sync_output() +{ + glong dt, total; + gint opt, opt_last; + struct timeval tv, tv_start, tv_last_change; + gboolean was_closed = !opened; + + if (!the_op->buffer_playing || !the_op->buffer_playing()) + { + DEBUG(("[crossfade] sync_output: nothing to do\n")); + return; + } + + DEBUG(("[crossfade] sync_output: waiting for plugin...\n")); + + dt = 0; + opt_last = 0; + gettimeofday(&tv_start, NULL); + gettimeofday(&tv_last_change, NULL); + + while ((dt < SYNC_OUTPUT_TIMEOUT) + && !stopped && output_opened && !(was_closed && opened) && the_op && the_op->buffer_playing()) + { + + /* use output_time() to check if the output plugin is still active */ + if (the_op->output_time) + { + opt = the_op->output_time(); + if (opt != opt_last) + { + /* output_time has changed */ + opt_last = opt; + gettimeofday(&tv_last_change, NULL); + } + else + { + /* calculate time since last change of the_op->output_time() */ + gettimeofday(&tv, NULL); + dt = (tv.tv_sec - tv_last_change.tv_sec) * 1000 + (tv.tv_usec - tv_last_change.tv_usec) / 1000; + } + } + + /* yield */ + MUTEX_UNLOCK(&buffer_mutex); + xfade_usleep(10000); + MUTEX_LOCK(&buffer_mutex); + } + + /* calculate total time we spent in here */ + gettimeofday(&tv, NULL); + total = (tv.tv_sec - tv_start.tv_sec) * 1000 + (tv.tv_usec - tv_start.tv_usec) / 1000; + + /* print some debug info */ + /* *INDENT-OFF* */ + if (stopped) + DEBUG(("[crossfade] sync_output: ... stopped\n")) + else if (was_closed && opened) + DEBUG(("[crossfade] sync_output: ... reopened\n")) + else if (dt >= SYNC_OUTPUT_TIMEOUT) + DEBUG(("[crossfade] sync_output: ... TIMEOUT! (%ld ms)\n", total)) + else + DEBUG(("[crossfade] sync_output: ... done (%ld ms)\n", total)); + /* *INDENT-ON* */ +} + +void * +buffer_thread_f(void *arg) +{ + gpointer data; + gint sync; + gint op_free; + gint length_bak, length, blen; + glong timeout, dt; + gboolean stopping; + + struct timeval tv; + struct timeval mark; + + DEBUG(("[crossfade] buffer_thread_f: thread started (pid=%d)\n", (int) getpid())); + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + while (!stopped) + { + /* yield */ +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] buffer_thread_f: yielding...\n")); +#endif + MUTEX_UNLOCK(&buffer_mutex); + xfade_usleep(10000); + MUTEX_LOCK(&buffer_mutex); + + /* --------------------------------------------------------------------- */ + + stopping = FALSE; + + /* V0.3.0: New timeout detection */ + if (!opened) + { + gboolean current = xfplayer_input_playing(); + + /* also see fini() */ + if (last_close.tv_sec || last_close.tv_usec) + { + gettimeofday(&tv, NULL); + timeout = (tv.tv_sec - last_close.tv_sec) * 1000 + + (tv.tv_usec - last_close.tv_usec) / 1000; + } + else + timeout = -1; + + if (current != input_playing) + { + input_playing = current; + + if (current) + DEBUG(("[crossfade] buffer_thread_f: input restarted after %ld ms\n", timeout)) + else + DEBUG(("[crossfade] buffer_thread_f: input stopped after + %ld ms\n", timeout)); + } + + /* 0.3.0: HACK: output_keep_opened: play silence during prebuffering */ + if (input_playing && config->output_keep_opened && (buffer->used == 0)) + { + buffer->silence = 0; + buffer->silence_len = MS2B(100); + } + + /* 0.3.9: Check for timeout only if we have not been stopped for restart */ + /* 0.3.11: When using the songchange hack, depend on it's output_restart + * flag. Without the hack, use the configuration's dialog songchange + * timeout setting instead of a fixed timeout value. */ + if (input_stopped_for_restart && !output_restart) + { + if (playing) + DEBUG(("[crossfade] buffer_thread_f: timeout:" + " stopping after %ld ms (songchange patch)\n", timeout)); + stopping = TRUE; + } + else if (((timeout < 0) || (timeout >= config->songchange_timeout && !output_restart)) && !input_playing) + { + if (playing) + DEBUG(("[crossfade] buffer_thread_f: timeout:" + " input did not restart after %ld ms\n", timeout)); + stopping = TRUE; + } + } + + /* V0.2.4: Moved the timeout checks in front of the buffer_free() check + * below. Before, buffer_thread_f could (theoretically) loop + * endlessly if buffer_free() returned 0 all the time. */ + + /* calculate time since last write to the buffer (ignore overflows) */ + gettimeofday(&tv, NULL); + timeout = (tv.tv_sec - last_write.tv_sec) * 1000 + + (tv.tv_usec - last_write.tv_usec) / 1000; + + /* check for timeout/eop (note this is the only way out of this loop) */ + if (stopping) + { + if (playing) + { + DEBUG(("[crossfade] buffer_thread_f: timeout: manual stop\n")); + + /* if CONFIG_STOP is of TYPE_NONE, immediatelly close the device... */ + if ((config->fc[FADE_CONFIG_STOP].type == FADE_TYPE_NONE) && !config->output_keep_opened) + break; + + /* special handling for pause */ + if (paused) + { + DEBUG(("[crossfade] buffer_thread_f: timeout: paused, closing now...\n")); + paused = FALSE; + if (config->output_keep_opened) + the_op->pause(0); + else + break; + } + else if (buffer->pause >= 0) + { + DEBUG(("[crossfade] buffer_thread_f: timeout: cancelling pause countdown\n")); + buffer->pause = -1; + } + + /* ...otherwise, do the fadeout first */ + xfade_apply_fade_config(&config->fc[FADE_CONFIG_STOP]); + + /* force CONFIG_START in case the user restarts playback during fadeout */ + fade_config = &config->fc[FADE_CONFIG_START]; + playing = FALSE; + eop = TRUE; + } + else + { + if (!eop) + { + DEBUG(("[crossfade] buffer_thread_f: timeout: end of playback\n")); + + /* 0.3.3: undo trailing gap killer at end of playlist */ + if (buffer->gap_killed) + { + buffer->used += buffer->gap_killed; + DEBUG(("[crossfade] buffer_thread_f: timeout:" + " undoing trailing gap (%d ms)\n", B2MS(buffer->gap_killed))); + } + + /* do the fadeout if applicable */ + if (config->fc[FADE_CONFIG_EOP].type != FADE_TYPE_NONE) + xfade_apply_fade_config(&config->fc[FADE_CONFIG_EOP]); + + fade_config = &config->fc[FADE_CONFIG_START]; /* see above */ + eop = TRUE; + } + + if (buffer->used == 0) + { + if (config->output_keep_opened) + { + /* 0.3.0: play silence while keeping the output opened */ + buffer->silence = 0; + buffer->silence_len = MS2B(100); + } + else if (buffer->silence_len <= 0) + { + sync_output(); + if (opened) + { + DEBUG(("[crossfade] buffer_thread_f: timeout, eop: device has been reopened\n")); + DEBUG(("[crossfade] buffer_thread_f: timeout, eop: -> continuing playback\n")); + eop = FALSE; + } + else + { + DEBUG(("[crossfade] buffer_thread_f: timeout, eop: closing output...\n")); + break; + } + } + } + } + } + else + eop = FALSE; + + /* --------------------------------------------------------------------- */ + + /* get free space in device output buffer + * NOTE: disk_writer always returns <big int> here */ + op_free = the_op->buffer_free() & -4; + + /* continue waiting if there is no room in the device buffer */ + if (op_free == 0) + continue; + + /* --- Limit OP buffer use (decreases latency) ------------------------- */ + + /* HACK: limit output plugin buffer usage to decrease latency */ + if (config->enable_op_max_used) + { + gint output_time = the_op->output_time(); + gint output_used = the_op->written_time() - output_time; + gint output_limit = MS2B(config->op_max_used_ms - MIN(output_used, config->op_max_used_ms)); + + if (output_flush_time != output_time) + { + /* slow down output, but always write _some_ data */ + if (output_limit < in_format.bps / 100 / 2) + output_limit = in_format.bps / 100 / 2; + + if (op_free > output_limit) + op_free = output_limit; + } + } + + /* --- write silence --------------------------------------------------- */ + + if (!paused && (buffer->silence <= 0) && (buffer->silence_len >= 4)) + { + /* write as much silence as a) there is left and b) the device can take */ + length = buffer->silence_len; + if (length > op_free) + length = op_free; + + /* make sure we always operate on stereo sample boundary */ + length &= -4; + + /* HACK: don't stay in here too long when in realtime mode (see below) */ + if (realtime) + gettimeofday(&mark, NULL); + + /* write length bytes to the device */ + length_bak = length; + while (length > 0) + { + data = zero_4k; + blen = sizeof(zero_4k); + if (blen > length) + blen = length; + + /* make sure zero_4k is cleared. The effect plugin within + * the output plugin may have modified this buffer! (0.2.8) */ + memset(zero_4k, 0, blen); + + /* HACK: the original OSS plugin hangs when writing large + * blocks (greater than device buffer size) in realtime mode */ + if (the_op_config.max_write_enable && (blen > the_op_config.max_write_len)) + blen = the_op_config.max_write_len; + + /* finally, write data */ + the_op->write_audio(data, blen); + length -= blen; + + /* HACK: don't stay in here too long (force yielding every 10 ms) */ + if (realtime) + { + gettimeofday(&tv, NULL); + dt = (tv.tv_sec - mark.tv_sec) * 1000 + + (tv.tv_usec - mark.tv_usec) / 1000; + if (dt >= 10) + break; + } + } + + /* calculate how many bytes actually have been written */ + length = length_bak - length; + } + + /* --- write data ------------------------------------------------- */ + + else if (!paused && (buffer->preload <= 0) && (buffer->used >= 4)) + { + /* write as much data as a) is available and b) the device can take */ + length = buffer->used; + if (length > op_free) + length = op_free; + + /* HACK: throttle output (used with fast output plugins) */ + if (the_op_config.throttle_enable && !realtime && opened) + { + sync = buffer->sync_size - (buffer->size - buffer->used); + if (sync < 0) length = 0; + else if (sync < length) length = sync; + } + + /* clip length to silence countdown (if applicable) */ + if ((buffer->silence >= 4) && (length > buffer->silence)) + length = buffer->silence; + + /* clip length to reopen countdown (if applicable) */ + if ((buffer->reopen >= 0) && (length > buffer->reopen)) + length = buffer->reopen; + + /* clip length to pause countdown (if applicable) */ + if ((buffer->pause >= 0) && (length > buffer->pause)) + length = buffer->pause; + + /* make sure we always operate on stereo sample boundary */ + length &= -4; + + /* HACK: don't stay in here too long when in realtime mode (see below) */ + if (realtime) + gettimeofday(&mark, NULL); + + /* write length bytes to the device */ + length_bak = length; + while (length > 0) + { + data = buffer->data + buffer->rd_index; + blen = buffer->size - buffer->rd_index; + if (blen > length) + blen = length; + + /* HACK: the original OSS plugin hangs when writing large + * blocks (greater than device buffer size) in realtime mode */ + if (the_op_config.max_write_enable && (blen > the_op_config.max_write_len)) + blen = the_op_config.max_write_len; + +#ifdef HAVE_LIBFFTW + /* fft playground */ + fft_flow(&fft_context, (gpointer) data, blen); +#endif + /* finally, write data */ + the_op->write_audio(data, blen); + + buffer->rd_index = (buffer->rd_index + blen) % buffer->size; + buffer->used -= blen; + length -= blen; + + /* HACK: don't stay in here too long (force yielding every 10 ms) */ + if (realtime) + { + gettimeofday(&tv, NULL); + dt = (tv.tv_sec - mark.tv_sec) * 1000 + (tv.tv_usec - mark.tv_usec) / 1000; + if (dt >= 10) + break; + } + } + + /* calculate how many bytes actually have been written */ + length = length_bak - length; + } + else + length = 0; + + /* update realtime throttling */ + output_written += length; + output_streampos += length; + + /* --- check countdowns ------------------------------------------------ */ + + if (buffer->silence > 0) + { + buffer->silence -= length; + if (buffer->silence < 0) + DEBUG(("[crossfade] buffer_thread_f: WARNING: silence overrun: %d\n", buffer->silence)); + } + else if (buffer->silence_len > 0) + { + buffer->silence_len -= length; + if (buffer->silence_len <= 0) + { + if (buffer->silence_len < 0) + DEBUG(("[crossfade] buffer_thread_f: WARNING: silence_len overrun: %d\n", + buffer->silence_len)); + } + } + + if ((buffer->reopen >= 0) && !((buffer->silence <= 0) && (buffer->silence_len > 0))) + { + buffer->reopen -= length; + if (buffer->reopen <= 0) + { + if (buffer->reopen < 0) + DEBUG(("[crossfade] buffer_thread_f: WARNING: reopen overrun: %d\n", buffer->reopen)); + + DEBUG(("[crossfade] buffer_thread_f: closing/reopening device\n")); + if (buffer->reopen_sync) + sync_output(); + + if (the_op->close_audio) + the_op->close_audio(); + + if (!the_op->open_audio(out_format.fmt, out_format.rate, out_format.nch)) + { + DEBUG(("[crossfade] buffer_thread_f: reopening output plugin failed!\n")); + g_free(buffer->data); + output_opened = FALSE; + MUTEX_UNLOCK(&buffer_mutex); + THREAD_EXIT(0); + return NULL; + } + + output_flush_time = 0; + output_written = 0; + output_streampos = 0; + + /* We need to take the leading gap killer into account here: + * It will fix streampos only after gapkilling has finished. + * So, if gapkilling is still in progress at this point, we + * have to fix it ourselves. */ + output_offset = buffer->used; + if ((buffer->gap_len > 0) && (buffer->gap > 0)) + output_offset += buffer->gap_len - buffer->gap; + output_offset = B2MS(output_offset) - xfade_written_time(); + + /* make sure reopen is not 0 */ + buffer->reopen = -1; + } + } + + if (buffer->pause >= 0) + { + buffer->pause -= length; + if (buffer->pause <= 0) + { + if (buffer->pause < 0) + DEBUG(("[crossfade] buffer_thread_f: WARNING: pause overrun: %d\n", buffer->pause)); + + DEBUG(("[crossfade] buffer_thread_f: pausing output\n")); + + paused = TRUE; + sync_output(); + + if (paused) + the_op->pause(1); + else + DEBUG(("[crossfade] buffer_thread_f: unpause during sync\n")) buffer->pause = -1; + } + } + } + + /* ----------------------------------------------------------------------- */ + + /* cleanup: close output */ + if (output_opened) + { + xfade_stop_monitor(); + + DEBUG(("[crossfade] buffer_thread_f: closing output...\n")); + + if (the_op->close_audio) + the_op->close_audio(); + + DEBUG(("[crossfade] buffer_thread_f: closing output... done\n")); + + g_free(buffer->data); + output_opened = FALSE; + } + else + DEBUG(("[crossfade] buffer_thread_f: output already closed!\n")); + + /* ----------------------------------------------------------------------- */ + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + + /* done */ + DEBUG(("[crossfade] buffer_thread_f: thread finished\n")); + THREAD_EXIT(0); + return NULL; +} + +void +xfade_close_audio() +{ + DEBUG(("[crossfade] close:\n")); + DEBUG(("[crossfade] close: playing=%d filename=%s\n", + xfplayer_input_playing(), xfplaylist_get_filename(xfplaylist_get_position()))); + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + /* sanity... the vorbis plugin likes to call close_audio() twice */ + if (!opened) + { + DEBUG(("[crossfade] close: WARNING: not opened!\n")); + MUTEX_UNLOCK(&buffer_mutex); + return; + } + +#if defined(COMPILE_FOR_AUDACIOUS) && AUDACIOUS_ABI_VERSION >= 2 + /* HACK: to distinguish between STOP and EOP, check Audacious' + input_playing() variable. It seems to be TRUE at this point + only when the end of the playlist is reached. + + Normally, 'playing' is constantly being updated in the + xfade_buffer_playing() callback, but Audacious does not seem + to use it. Therefore, we can set 'playing' to FALSE here, + which later 'buffer_thread' will interpret as EOP (see above). + */ + if (xfplayer_input_playing()) + playing = FALSE; +#else + /* HACK: use patched XMMS 'input_stopped_for_restart' */ + if (input_stopped_for_restart && *input_stopped_for_restart) + { + DEBUG(("[crossfade] close: playback will restart soon\n")); + output_restart = TRUE; + } + else + output_restart = FALSE; +#endif + + if (playing) + { + /* immediatelly close output when paused */ + if (paused) + { + buffer->pause = -1; + paused = FALSE; + if (config->output_keep_opened) + { + buffer->used = 0; + the_op->flush(0); + the_op->pause(0); + } + else + stopped = TRUE; + } + + /* HACK: If playlist_get_info_going is not true here, + * XMMS is about to exit. In this case, we stop + * the buffer thread before returning from this + * function. Otherwise, SEGFAULT may occur when + * XMMS tries to cleanup an output plugin which + * we are still using. + * + * NOTE: This hack has become obsolete as of 0.3.5. + * See output_list_hack(). + * + * NOTE: Not quite. There still are some problems when + * XMMS is exitting while a song is playing. So + * this HACK has been enabled again. + * + * NOTE: Another thing: If output_keep_opened is enabled, + * close_audio() is never called, so that the patch + * can not work. + */ +#if 1 + if ((xmms_is_quitting && *xmms_is_quitting) + || (xmms_playlist_get_info_going && !*xmms_playlist_get_info_going)) + { + DEBUG(("[crossfade] close: stop (about to quit)\n")) + + /* wait for buffer thread to clean up and terminate */ + stopped = TRUE; +#if 1 + MUTEX_UNLOCK(&buffer_mutex); + if (THREAD_JOIN(buffer_thread)) + PERROR("[crossfade] close: phtread_join()"); + MUTEX_LOCK(&buffer_mutex); +#else + while (output_opened) + { + MUTEX_UNLOCK(&buffer_mutex); + xfade_usleep(10000); + MUTEX_LOCK(&buffer_mutex); + } +#endif + } + else +#endif + DEBUG(("[crossfade] close: stop\n")); + + fade_config = &config->fc[FADE_CONFIG_MANUAL]; + } + else + { + /* gint x = *((gint *)0); */ /* force SEGFAULT for debugging */ + DEBUG(("[crossfade] close: songchange/eop\n")); + + /* kill trailing gap (does not use buffer->gap_*) */ + if (output_opened && xfade_cfg_gap_trail_enable(config)) + { + gint gap_len = MS2B(xfade_cfg_gap_trail_len(config)) & -4; + gint gap_level = xfade_cfg_gap_trail_level(config); + gint length = MIN(gap_len, buffer->used); + + /* DEBUG(("[crossfade] close: len=%d level=%d length=%d\n", gap_len, gap_level, length)); */ + + buffer->gap_killed = 0; + while (length > 0) + { + gint wr_xedni = (buffer->rd_index + buffer->used - 1) % buffer->size + 1; + gint blen = MIN(length, wr_xedni); + gint16 *p = buffer->data + wr_xedni, left, right; + gint index = 0; + + while (index < blen) + { + right = *--p; + left = *--p; + if (ABS(left) >= gap_level) break; + if (ABS(right) >= gap_level) break; + index += 4; + } + + buffer->used -= index; + buffer->gap_killed += index; + + if (index < blen) + break; + length -= blen; + } + + DEBUG(("[crossfade] close: trailing gap size: %d/%d ms\n", B2MS(buffer->gap_killed), B2MS(gap_len))); + } + + /* skip to previous zero crossing */ + if (output_opened && config->gap_crossing) + { + int crossing; + + buffer->gap_skipped = 0; + for (crossing = 0; crossing < 4; crossing++) + { + while (buffer->used > 0) + { + gint wr_xedni = (buffer->rd_index + buffer->used - 1) % buffer->size + 1; + gint blen = MIN(buffer->used, wr_xedni); + gint16 *p = buffer->data + wr_xedni, left; + gint index = 0; + + while (index < blen) + { + left = (--p, *--p); + if ((crossing & 1) ^ (left > 0)) + break; + index += 4; + } + + buffer->used -= index; + buffer->gap_skipped += index; + + if (index < blen) + break; + } + } + DEBUG(("[crossfade] close: skipped %d bytes to previous zero crossing\n", buffer->gap_skipped)); + + /* update gap_killed (for undoing gap_killer in case of EOP) */ + buffer->gap_killed += buffer->gap_skipped; + } + + fade_config = &config->fc[FADE_CONFIG_XFADE]; + } + + /* XMMS has left the building */ + opened = FALSE; + + /* update last_close */ + gettimeofday(&last_close, NULL); + input_playing = FALSE; + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); +} + +void +xfade_flush(gint time) +{ + gint pos; + gchar *file; + + DEBUG(("[crossfade] flush: time=%d\n", time)); + + /* get filename */ + pos = xfplaylist_get_position(); + file = xfplaylist_get_filename(pos); + + if (!file) + file = g_strdup(xfplaylist_get_songtitle(pos)); + +#if defined(COMPILE_FOR_AUDACIOUS) + /* HACK: special handling for audacious, which just calls flush(0) on a songchange */ + if (file && last_filename && strcmp(file, last_filename) != 0) + { + DEBUG(("[crossfade] flush: filename changed, forcing close/reopen...\n")); + xfade_close_audio(); + /* 0.3.14: xfade_close_audio sets fade_config to FADE_CONFIG_MANUAL, + * but this is an automatic songchange */ + fade_config = &config->fc[FADE_CONFIG_XFADE]; + xfade_open_audio(in_format.fmt, in_format.rate, in_format.nch); + DEBUG(("[crossfade] flush: filename changed, forcing close/reopen... done\n")); + return; + } +#endif + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + /* update streampos with new stream position (input format size) */ + streampos = ((gint64) time * in_format.bps / 1000) & -4; + + /* flush output device / apply seek crossfade */ + if (config->fc[FADE_CONFIG_SEEK].type == FADE_TYPE_FLUSH) + { + /* flush output plugin */ + the_op->flush(time); + output_flush_time = time; + output_streampos = MS2B(time); + + /* flush buffer, disable leading gap killing */ + buffer_reset(buffer, config); + } + else if (paused) + { + fade_config_t fc; + + /* clear buffer */ + buffer->used = 0; + + /* apply only the fade_in part of FADE_CONFIG_PAUSE */ + memcpy(&fc, &config->fc[FADE_CONFIG_PAUSE], sizeof(fc)); + fc.out_len_ms = 0; + fc.ofs_custom_ms = 0; + xfade_apply_fade_config(&fc); + } + else + xfade_apply_fade_config(&config->fc[FADE_CONFIG_SEEK]); + + /* restart realtime throttling (should find another name for that var) */ + output_written = 0; + + /* make sure that the gapkiller is disabled */ + buffer->gap = 0; + + /* update output offset */ + output_offset = the_op->written_time() - time + B2MS(buffer->used) + B2MS(buffer->silence_len); + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] flush: time=%d: done.\n", time)); +#endif +} + +void +xfade_pause(short p) +{ + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + if (p) + { + fade_config_t *fc = &config->fc[FADE_CONFIG_PAUSE]; + if (fc->type == FADE_TYPE_PAUSE_ADV) + { + int fade, length, n; + int index = buffer->rd_index; + int out_len = MS2B(xfade_cfg_fadeout_len(fc)) & -4; + int in_len = MS2B(xfade_cfg_fadein_len(fc)) & -4; + int silence_len = MS2B(xfade_cfg_offset(fc)) & -4; + + /* limit fadeout/fadein len to available data in buffer */ + if ((out_len + in_len) > buffer->used) + { + out_len = (buffer->used / 2) & -4; + in_len = out_len; + } + + DEBUG(("[crossfade] pause: paused=1 out=%d in=%d silence=%d\n", + B2MS(out_len), B2MS(in_len), B2MS(silence_len))); + + /* fade out (modifies buffer directly) */ + fade = 0; + length = out_len; + while (length > 0) + { + gint16 *p = buffer->data + index; + gint blen = buffer->size - index; + if (blen > length) + blen = length; + + for (n = blen / 4; n > 0; n--) + { + gfloat factor = 1.0f - ((gfloat) fade / out_len); + *p = (gfloat)*p * factor; p++; + *p = (gfloat)*p * factor; p++; + fade += 4; + } + + index = (index + blen) % buffer->size; + length -= blen; + } + + /* fade in (modifies buffer directly) */ + fade = 0; + length = in_len; + while (length > 0) + { + gint16 *p = buffer->data + index; + gint blen = buffer->size - index; + if (blen > length) + blen = length; + + for (n = blen / 4; n > 0; n--) + { + gfloat factor = (gfloat) fade / in_len; + *p = (gfloat)*p * factor; p++; + *p = (gfloat)*p * factor; p++; + fade += 4; + } + + index = (index + blen) % buffer->size; + length -= blen; + } + + /* start silence and pause countdowns */ + buffer->silence = out_len; + buffer->silence_len = silence_len; + buffer->pause = out_len + silence_len; + paused = FALSE; /* (!) will be set to TRUE in buffer_thread_f */ + } + else + { + the_op->pause(1); + paused = TRUE; + DEBUG(("[crossfade] pause: paused=1\n")); + } + } + else + { + the_op->pause(0); + buffer->pause = -1; + paused = FALSE; + DEBUG(("[crossfade] pause: paused=0\n")); + } + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); +} + +gint +xfade_buffer_free() +{ + gint size, free; + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] buffer_free:\n")); +#endif + + /* sanity check */ + if (!output_opened) + { + DEBUG(("[crossfade] buffer_free: WARNING: output closed!\n")); + return buffer->sync_size; + } + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + size = buffer->size; + + /* When running in realtime mode, we need to take special care here: + * While XMMS is writing data to the output plugin, it will not yield + * ANY processing time to the buffer thread. It will only stop when + * xfade_buffer_free() no longer reports free buffer space. */ + if (realtime) + { + gint64 wanted = output_written + buffer->preload_size; + + /* Fix for XMMS misbehaviour (possibly a bug): If the free space as + * returned by xfade_buffer_free() is below a certain minimum block size + * (tests showed 2304 bytes), XMMS will not send more data until there + * is enough room for one of those blocks. + * + * This breaks preloading in realtime mode. To make sure that the pre- + * load buffer gets filled we request additional sync_size bytes. */ + wanted += buffer->sync_size; + + if (wanted <= size) + size = wanted; + } + + free = size - buffer->used; + if (free < 0) + free = 0; + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + + /* Convert to input format size. For input rates > output rate this will + * return less free space than actually is available, but we don't care. */ + free /= (out_format.rate / (in_format.rate + 1)) + 1; + if (in_format.is_8bit) + free /= 2; + if (in_format.nch == 1) + free /= 2; + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] buffer_free: %d\n", free)); +#endif + return free; +} + +gint +xfade_buffer_playing() +{ + /* always return FALSE here (if not in pause) so XMMS immediatelly + * starts playback of the next song */ + + /* + * NOTE: this causes trouble when playing HTTP audio streams. + * + * mpg123.lib will start prebuffering (and thus stalling output) when both + * 1) it's internal buffer is emptied (does happen all the time) + * 2) the output plugin's buffer_playing() (this function) returns FALSE + */ + + if (paused) + playing = TRUE; + else + playing = + (is_http && (buffer->used > 0) && the_op->buffer_playing()) + || (buffer->reopen >= 0) + || (buffer->silence > 0) + || (buffer->silence_len > 0); + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] buffer_playing: %d\n", playing)); +#endif + return playing; +} + +gint +xfade_written_time() +{ + if (!output_opened) + return 0; + return (gint) (streampos * 1000 / in_format.bps); +} + +gint +xfade_output_time() +{ + gint time; + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] output_time:\n")); +#endif + + /* sanity check (note: this one _does_ happen all the time) */ + if (!output_opened) + return 0; + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + time = the_op->output_time() - output_offset; + if (time < 0) + time = 0; + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + +#ifdef DEBUG_HARDCORE + DEBUG(("[crossfade] output_time: time=%d\n", time)); +#endif + return time; +} + +void +xfade_cleanup() +{ + DEBUG(("[crossfade] cleanup:\n")); + + /* lock buffer */ + MUTEX_LOCK(&buffer_mutex); + + /* check if buffer thread is still running */ + if (output_opened) + { + DEBUG(("[crossfade] cleanup: closing output\n")); + + stopped = TRUE; + + /* wait for buffer thread to clean up and terminate */ + MUTEX_UNLOCK(&buffer_mutex); + if (THREAD_JOIN(buffer_thread)) + PERROR("[crossfade] close: thread_join()"); + MUTEX_LOCK(&buffer_mutex); + } + + /* unlock buffer */ + MUTEX_UNLOCK(&buffer_mutex); + + DEBUG(("[crossfade] cleanup: done\n")); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/crossfade.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,581 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef _CROSSFADE_H_ +#define _CROSSFADE_H_ + +#define COMPILE_FOR_AUDACIOUS +#define AUDACIOUS_ABI_VERSION 2 + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if !defined(HAVE_GLIB_THREADS) +# include <pthread.h> +#endif +#include <glib.h> + +#include <audacious/plugin.h> +#include <audacious/playlist.h> +#include <audacious/auddrct.h> +#include <audacious/configdb.h> +#include <audacious/util.h> + +#include "debug.h" + +#undef VOLUME_NORMALIZER +#define TIMING_COMMENTS + +#define OUTPUT_RATE the_rate +#define OUTPUT_NCH 2 +#define OUTPUT_BPS (OUTPUT_RATE * OUTPUT_NCH * 2) + +#if defined(HAVE_LIBSAMPLERATE) +# define MAX_RATE 192000 +#else +# define MAX_RATE 48000 +#endif + +#define SYNC_OUTPUT_TIMEOUT 2000 + +#define OUTPUT_METHOD_BUILTIN_OSS 0 +#define OUTPUT_METHOD_PLUGIN 1 +#define OUTPUT_METHOD_BUILTIN_NULL 2 + +#define FADE_CONFIG_XFADE 0 +#define FADE_CONFIG_MANUAL 1 +#define FADE_CONFIG_ALBUM 2 +#define FADE_CONFIG_START 3 +#define FADE_CONFIG_STOP 4 +#define FADE_CONFIG_EOP 5 +#define FADE_CONFIG_SEEK 6 +#define FADE_CONFIG_PAUSE 7 +#define FADE_CONFIG_TIMING 8 +#define MAX_FADE_CONFIGS 9 + +#define FADE_TYPE_REOPEN 0 +#define FADE_TYPE_FLUSH 1 +#define FADE_TYPE_NONE 2 +#define FADE_TYPE_PAUSE 3 +#define FADE_TYPE_SIMPLE_XF 4 +#define FADE_TYPE_ADVANCED_XF 5 +#define FADE_TYPE_FADEIN 6 +#define FADE_TYPE_FADEOUT 7 +#define FADE_TYPE_PAUSE_NONE 8 +#define FADE_TYPE_PAUSE_ADV 9 +#define MAX_FADE_TYPES 10 + +#define TYPEMASK_XFADE ((1 << FADE_TYPE_REOPEN) | \ + (1 << FADE_TYPE_NONE) | \ + (1 << FADE_TYPE_PAUSE) | \ + (1 << FADE_TYPE_SIMPLE_XF) | \ + (1 << FADE_TYPE_ADVANCED_XF)) + +#define TYPEMASK_MANUAL ((1 << FADE_TYPE_REOPEN) | \ + (1 << FADE_TYPE_FLUSH) | \ + (1 << FADE_TYPE_NONE) | \ + (1 << FADE_TYPE_PAUSE) | \ + (1 << FADE_TYPE_SIMPLE_XF) | \ + (1 << FADE_TYPE_ADVANCED_XF)) + +#define TYPEMASK_ALBUM ((1 << FADE_TYPE_NONE)) + +#define TYPEMASK_START ((1 << FADE_TYPE_NONE) | \ + (1 << FADE_TYPE_FADEIN)) + +#define TYPEMASK_STOP ((1 << FADE_TYPE_NONE) | \ + (1 << FADE_TYPE_FADEOUT)) + +#define TYPEMASK_EOP ((1 << FADE_TYPE_NONE) | \ + (1 << FADE_TYPE_FADEOUT)) + +#define TYPEMASK_SEEK ((1 << FADE_TYPE_FLUSH) | \ + (1 << FADE_TYPE_NONE) | \ + (1 << FADE_TYPE_SIMPLE_XF)) + +#define TYPEMASK_PAUSE ((1 << FADE_TYPE_PAUSE_NONE) | \ + (1 << FADE_TYPE_PAUSE_ADV)) + +#define TYPEMASK_TIMING (0) + +#define FC_OFFSET_NONE 0 +#define FC_OFFSET_LOCK_IN 1 +#define FC_OFFSET_LOCK_OUT 2 +#define FC_OFFSET_CUSTOM 3 + +#define DEFAULT_OSS_ALT_AUDIO_DEVICE "/dev/dsp" +#define DEFAULT_OSS_ALT_MIXER_DEVICE "/dev/mixer" +#define DEFAULT_OP_CONFIG_STRING "libOSS.so=0,1,2304,0; libdisk_writer.so=1,0,2304,1" +#define DEFAULT_OP_NAME "libALSA.so" +#define DEFAULT_EP_NAME "libnormvol.so" + +#define DEFAULT_OP_CONFIG \ +{ FALSE, FALSE, 2304, FALSE } + +#define CONFIG_DEFAULT \ +{ 0, /* output_method */ \ + 44100, /* output_rate */ \ + 2, /* output_quality */ \ + 0, /* oss_audio_device */ \ + FALSE, /* oss_use_alt_audio_device */ \ + NULL, /* oss_alt_audio_device */ \ + 0, /* oss_mixer_device */ \ + FALSE, /* oss_use_alt_mixer_device */ \ + NULL, /* oss_alt_mixer_device */ \ + FALSE, /* oss_mixer_use_master */ \ + 0, /* oss_buffer_size_ms */ \ + 250, /* oss_preload_size_ms */ \ + 22, /* oss_fragments */ \ + 12, /* oss_fragment_size */ \ + FALSE, /* oss_maxbuf_enable */ \ + NULL, /* op_config_string */ \ + NULL, /* op_name */ \ + NULL, /* ep_name */ \ + FALSE, /* ep_enable */ \ + TRUE, /* volnorm_enable */ \ + 8000, /* volnorm_target */ \ + FALSE, /* volnorm_use_qa */ \ + 10000, /* mix_size_ms */ \ + TRUE, /* mix_size_auto */ \ + \ + { /* fc[MAX_FADE_CONFIGS] */ \ + { FADE_CONFIG_XFADE, /* config */ \ + FADE_TYPE_ADVANCED_XF, /* type */ \ + 2000, /* pause_len_ms */ \ + 6000, /* simple_len_ms */ \ + TRUE, 4000, 0, /* out_enable, _len_ms, _volume */ \ + FC_OFFSET_CUSTOM, /* ofs_type */ \ + FC_OFFSET_CUSTOM, -6000, /* ofs_type_wanted, ofs_custom_ms */ \ + TRUE, /* in_locked */ \ + FALSE, 4000, 33, /* in_enable, _len_ms, _volume */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + FALSE, /* flush */ \ + TYPEMASK_XFADE /* type_mask */ \ + }, \ + { FADE_CONFIG_MANUAL, /* config */ \ + FADE_TYPE_SIMPLE_XF, /* type */ \ + 2000, /* pause_len_ms */ \ + 1000, /* simple_len_ms */ \ + TRUE, 500, 0, /* out_enable, _len_ms, _volume */ \ + FC_OFFSET_CUSTOM, /* ofs_type */ \ + FC_OFFSET_CUSTOM, -500, /* ofs_type_wanted, ofs_custom_ms */ \ + TRUE, /* in_locked */ \ + FALSE, 500, 50, /* in_enable, _len_ms, _volume */ \ + FALSE, /* flush_pause_enable */ \ + 500, /* flush_in_len_ms */ \ + FALSE, /* flush_in_enable */ \ + 500, 0, /* flush_in_len_ms, _volume */ \ + TRUE, /* flush */ \ + TYPEMASK_MANUAL /* type_mask */ \ + }, \ + { FADE_CONFIG_ALBUM, /* config */ \ + FADE_TYPE_NONE, /* type */ \ + 0, /* - */ \ + 0, /* - */ \ + FALSE, 0, 0, /* - */ \ + FC_OFFSET_NONE, /* - */ \ + FC_OFFSET_NONE, 0, /* - */ \ + FALSE, /* - */ \ + FALSE, 1000, 0, /* - */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + FALSE, /* flush */ \ + TYPEMASK_ALBUM /* type_mask */ \ + }, \ + { FADE_CONFIG_START, /* config */ \ + FADE_TYPE_FADEIN, /* type */ \ + 0, /* - */ \ + 0, /* - */ \ + FALSE, 0, 0, /* - */ \ + FC_OFFSET_NONE, /* - */ \ + FC_OFFSET_NONE, 0, /* - */ \ + FALSE, /* - */ \ + FALSE, 100, 0, /* - in_len_ms, _volume */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + TRUE, /* flush */ \ + TYPEMASK_START /* type_mask */ \ + }, \ + { FADE_CONFIG_STOP, /* config */ \ + FADE_TYPE_FADEOUT, /* type */ \ + 0, /* - */ \ + 0, /* - */ \ + FALSE, 100, 0, /* - out_len_ms, _volume */ \ + FC_OFFSET_NONE, /* - */ \ + FC_OFFSET_NONE, 500, /* - ofs_custom_ms */ \ + FALSE, /* - */ \ + FALSE, 0, 0, /* - */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + TRUE, /* flush */ \ + TYPEMASK_STOP /* type_mask */ \ + }, \ + { FADE_CONFIG_EOP, /* config */ \ + FADE_TYPE_FADEOUT, /* type */ \ + 0, /* - */ \ + 0, /* - */ \ + FALSE, 100, 0, /* - out_len_ms, _volume */ \ + FC_OFFSET_NONE, /* - */ \ + FC_OFFSET_NONE, 500, /* - ofs_custom_ms */ \ + FALSE, /* - */ \ + FALSE, 0, 0, /* - */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + FALSE, /* flush */ \ + TYPEMASK_EOP /* type_mask */ \ + }, \ + { FADE_CONFIG_SEEK, /* config */ \ + FADE_TYPE_SIMPLE_XF, /* type */ \ + 0, /* - */ \ + 50, /* simple_len_ms */ \ + FALSE, 0, 0, /* - */ \ + FC_OFFSET_NONE, /* - */ \ + FC_OFFSET_NONE, 0, /* - */ \ + FALSE, /* - */ \ + FALSE, 1000, 0, /* - */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + TRUE, /* flush */ \ + TYPEMASK_SEEK /* type_mask */ \ + }, \ + { FADE_CONFIG_PAUSE, /* config */ \ + FADE_TYPE_PAUSE_ADV, /* type */ \ + 0, /* - */ \ + 0, /* - */ \ + TRUE, 100, 0, /* - out_len_ms, - */ \ + FC_OFFSET_NONE, /* - */ \ + FC_OFFSET_NONE, 100, /* - ofs_custom_ms */ \ + FALSE, /* - */ \ + TRUE, 100, 0, /* - in_len_ms, - */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + FALSE, /* - */ \ + TYPEMASK_PAUSE /* type_mask */ \ + }, \ + { FADE_CONFIG_TIMING, /* config */ \ + FADE_TYPE_ADVANCED_XF, /* type */ \ + 0, /* - */ \ + 0, /* - */ \ + TRUE, 0, 0, /* out_enable, _len_ms, _volume */ \ + FC_OFFSET_CUSTOM, /* - */ \ + FC_OFFSET_CUSTOM, 0, /* - ofs_custom_ms */ \ + FALSE, /* - */ \ + TRUE, 0, 0, /* in_enable, _len_ms, _volume */ \ + FALSE, /* - */ \ + 0, /* - */ \ + FALSE, /* - */ \ + 0, 0, /* - */ \ + FALSE, /* flush */ \ + TYPEMASK_TIMING, /* type_mask */ \ + 500, /* out_skip_ms */ \ + 500 /* in_skip_ms */ \ + } \ + }, \ + TRUE, /* gap_lead_enable */ \ + 500, /* gap_lead_len_ms */ \ + 512, /* gap_lead_level */ \ + TRUE, /* gap_trail_enable */ \ + 500, /* gap_trail_len_ms */ \ + 512, /* gap_trail_level */ \ + TRUE, /* gap_trail_locked */ \ + TRUE, /* gap_crossing */ \ + FALSE, /* enable_debug */ \ + FALSE, /* enable_monitor */ \ + TRUE, /* enable_mixer */ \ + FALSE, /* mixer_reverse */ \ + FALSE, /* mixer_software */ \ + 75, /* mixer_vol_left */ \ + 75, /* mixer_vol_right */ \ + 500, /* songchange_timeout */ \ + 0, /* preload_size_ms */ \ + TRUE, /* album detection */ \ + FALSE, /* no_xfade_if_same_file */ \ + FALSE, /* enable_http_workaround */ \ + FALSE, /* enable_op_max_used */ \ + 250, /* op_max_used_ms */ \ + FALSE, /* output_keep_opened */ \ + NULL, /* presets */ \ + 250 /* sync_size_ms */ \ +} + + +#define DEBUG(x) { if (config->enable_debug) debug x; } +#define PERROR(x) { if (config->enable_debug) perror(x); } + +#define WRAP(x,n) (((x)<0)?((n)-(x))%(n):((x)%(n))) +#define B2MS(x) ((gint)((gint64)(x)*1000/OUTPUT_BPS)) +#define MS2B(x) ((gint)((gint64)(x)*OUTPUT_BPS/1000)) + + +/* get plugin info (imported by XMMS) */ +OutputPlugin *get_oplugin_info(); +OutputPlugin *get_crossfade_oplugin_info(); + +#include "player.h" + +/* utilities */ +void xfade_usleep(gint usec); + +/* config change callbacks */ +void xfade_realize_config(); +void xfade_realize_ep_config(); + + +typedef struct +{ + gint mix_size; /* mixing buffer length */ + gint sync_size; /* additional buffer space for mix timing */ + gint preload_size; /* preload buffer length */ + + /* ---------------------------------------------------------------------- */ + + gpointer data; /* buffer */ + gint size; /* total buffer length */ + + /* ---------------------------------------------------------------------- */ + + gint used; /* length */ + gint rd_index; /* offset */ + + gint preload; /* > 0: preloading */ + + /* ---------------------------------------------------------------------- */ + + gint mix; /* > 0: mixing new data into buffer */ + + gint fade; /* > 0: fading in new data */ + gint fade_len; /* length of fadein */ + gfloat fade_scale; /* 1.0f - in_level */ + +#define GAP_SKIPPING_POSITIVE -1 +#define GAP_SKIPPING_NEGATIVE -2 +#define GAP_SKIPPING_DONE -3 + gint gap; /* > 0: removing (leading) gap */ + gint gap_len; /* max. len of gap, 0=disabled */ + gint gap_level; /* max. sample value+1 to be considered "silent" */ + gint gap_killed; /* number of bytes that were killed last time */ + gint gap_skipped; /* negative/positive samples skipped */ + + gint skip; /* > 0: skipping beginning of song */ + gint skip_len; /* total number of samples to skip */ + + /* ---------------------------------------------------------------------- */ + + gint silence; /* > 0: delay until start of silence */ + gint silence_len; /* > 0: inserting silence */ + + gint reopen; /* >= 0: countdown to reopen device (disk_writer hack) */ + gboolean reopen_sync; /* TRUE: sync output plugin before reopening */ + + gint pause; /* >= 0: countdown to pause output plugin */ +} +buffer_t; + +typedef struct +{ + gint config; // one of FADE_CONFIG_*, constant + gint type; // one of FADE_TYPE_* + + gint pause_len_ms; // PAUSE + + gint simple_len_ms; // SIMPLE_XF + + gboolean out_enable; // ADVANCED_XF + gint out_len_ms; // ADVANCED_XF, FADEOUT, PAUSE + gint out_volume; // ADVANCED_XF, FADEOUT + + gint ofs_type; // ADVANCED_XF + gint ofs_type_wanted; // ADVANCED_XF + gint ofs_custom_ms; // ADVANCED_XF, FADEOUT (additional silence), PAUSE + + gboolean in_locked; // ADVANCED_XF + gboolean in_enable; // ADVANCED_XF, FLUSH + gint in_len_ms; // ADVANCED_XF, FLUSH, FADEIN, PAUSE + gint in_volume; // ADVANCED_XF, FLUSH, FADEIN + + gboolean flush_pause_enable; // FLUSH + gint flush_pause_len_ms; // FLUSH + gboolean flush_in_enable; // FLUSH + gint flush_in_len_ms; // FLUSH + gint flush_in_volume; // FLUSH + + /* additional stuff which is not configureable by the user */ + gboolean flush; // TRUE for manual, FALSE for xfade config + guint32 type_mask; // bitmask for FADE_TYPEs + + /* new fields as of v0.3.11: skip beginning/end of song */ + gint out_skip_ms; + gint in_skip_ms; +} +fade_config_t; + +typedef struct +{ + gboolean throttle_enable; + gboolean max_write_enable; + gint max_write_len; + gboolean force_reopen; +} +plugin_config_t; + +typedef struct +{ + /* output: method */ + gint output_method; + gint output_rate; + gint output_quality; + + /* output: builtin OSS */ + gint oss_audio_device; + gboolean oss_use_alt_audio_device; + gchar *oss_alt_audio_device; + + gint oss_mixer_device; + gboolean oss_use_alt_mixer_device; + gchar *oss_alt_mixer_device; + gboolean oss_mixer_use_master; + + gint oss_buffer_size_ms; + gint oss_preload_size_ms; + + gint oss_fragments; + gint oss_fragment_size; + gboolean oss_maxbuf_enable; + + /* output: plugin */ + gchar *op_config_string; /* stores configs for all plugins */ + gchar *op_name; /* name of the current plugin */ + + /* effects */ + gchar *ep_name; + gboolean ep_enable; + gboolean volnorm_enable; + gboolean volnorm_use_qa; + gint volnorm_target; + + /* crossfader */ + gint mix_size_ms; + gboolean mix_size_auto; + fade_config_t fc[MAX_FADE_CONFIGS]; + + /* gap killer */ + gboolean gap_lead_enable; + gint gap_lead_len_ms; + gint gap_lead_level; + + gboolean gap_trail_enable; + gint gap_trail_len_ms; + gint gap_trail_level; + gboolean gap_trail_locked; + + gboolean gap_crossing; + + /* misc */ + gboolean enable_debug; + gboolean enable_monitor; + + gboolean enable_mixer; + gboolean mixer_reverse; + gboolean mixer_software; + gint mixer_vol_left; + gint mixer_vol_right; + + gint songchange_timeout; + gint preload_size_ms; + gboolean album_detection; + gboolean no_xfade_if_same_file; + gboolean enable_http_workaround; + gboolean enable_op_max_used; + gint op_max_used_ms; + gboolean output_keep_opened; + + /* presets */ + GList *presets; + + /* additional stuff which is not configureable by the user */ + gint sync_size_ms; + + /* additional stuff which is not saved to the config file */ + gint page; + gint oss_page; + gint xf_index; +} +config_t; + + +/* some global vars... we should really get rid of those somehow */ +extern config_t *config; +extern config_t config_default; +extern buffer_t *buffer; + +#if defined(HAVE_GLIB_THREADS) +# define MUTEX GStaticMutex +# define MUTEX_LOCK(mutex) g_static_mutex_lock(mutex) +# define MUTEX_UNLOCK(mutex) g_static_mutex_unlock(mutex) +# define MUTEX_INITIALIZER G_STATIC_MUTEX_INIT +# define THREAD GThread * +# define THREAD_CREATE(thread,func) !(thread = g_thread_create(func, NULL, TRUE, NULL)) +# define THREAD_EXIT(rval) g_thread_exit(rval) +# define THREAD_JOIN(thread) g_thread_join(thread) +# define SCHED_YIELD g_thread_yield() +#else +# define MUTEX pthread_mutex_t +# define MUTEX_LOCK(mutex) pthread_mutex_lock(mutex) +# define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(mutex) +# define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +# define THREAD pthread_t +# define THREAD_CREATE(thread,func) pthread_create(&thread,NULL,func,NULL) +# define THREAD_EXIT(rval) pthread_exit(rval) +# define THREAD_JOIN(thread) pthread_join(thread,NULL) +# if defined(_POSIX_PRIORITY_SCHEDULING) +# define SCHED_YIELD sched_yield() +# else +# define SCHED_YIELD { } +# endif +#endif + +extern gboolean opened; /* XMMS-crossfade is opened */ +extern gboolean output_opened; /* the output plugin is opened */ +extern gint output_offset; +extern gint64 output_streampos; + +extern OutputPlugin *the_op; +extern gint the_rate; + +#endif /* _CROSSFADE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/debug.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,35 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#include <stdio.h> +#include <stdarg.h> + +void +debug(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/debug.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,34 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#ifdef __GNUC__ +/* enable -Wformat with debug() */ +void debug(char *format, ...) __attribute__ ((format(printf, 1, 2))); +#else +void debug(char *format, ...); +#endif + +#endif /* __DEBUG_H__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/format.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,163 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include "format.h" + +gboolean +setup_format(AFormat fmt, gint rate, gint nch, format_t * format) +{ + format->bps = 0; + format->fmt = fmt; + format->is_8bit = FALSE; + format->is_swapped = FALSE; + format->is_unsigned = FALSE; + + /* check format */ + switch (fmt) + { + case FMT_U8: + format->is_8bit = TRUE; + format->is_unsigned = TRUE; + break; + case FMT_S8: + format->is_8bit = TRUE; + break; + case FMT_U16_LE: + format->is_unsigned = TRUE; +#ifdef WORDS_BIGENDIAN + format->is_swapped = TRUE; +#endif + break; + case FMT_U16_BE: + format->is_unsigned = TRUE; +#ifndef WORDS_BIGENDIAN + format->is_swapped = TRUE; +#endif + break; + case FMT_U16_NE: + format->is_unsigned = TRUE; + break; + case FMT_S16_LE: +#ifdef WORDS_BIGENDIAN + format->is_swapped = TRUE; +#endif + break; + case FMT_S16_BE: +#ifndef WORDS_BIGENDIAN + format->is_swapped = TRUE; +#endif + break; + case FMT_S16_NE: + break; + default: + DEBUG(("[crossfade] setup_format: unknown format (%d)!\n", fmt)); + return -1; + } + + /* check rate */ + if ((rate < 1) || (rate > MAX_RATE)) + { + DEBUG(("[crossfade] setup_format: illegal rate (%d)!\n", rate)); + return -1; + } + format->rate = rate; + + /* check channels */ + switch (nch) + { + case 1: + case 2: + format->nch = nch; + break; + default: + DEBUG(("[crossfade] setup_format: illegal # of channels (%d)!\n", nch)); + return -1; + } + + /* calculate bps */ + format->bps = rate * nch; + if (!format->is_8bit) + format->bps *= 2; + + return 0; +} + +gboolean +format_match(AFormat fmt1, AFormat fmt2) +{ + if (fmt1 == fmt2) + return TRUE; /* exact match */ + + if ((fmt2 == FMT_U16_NE) || (fmt2 == FMT_S16_NE)) + { + AFormat fmt = fmt1; + fmt1 = fmt2; + fmt2 = fmt; + } + +#ifdef WORDS_BIGENDIAN + if (((fmt1 == FMT_U16_NE) && (fmt2 == FMT_U16_BE)) || ((fmt1 == FMT_S16_NE) && (fmt2 == FMT_S16_BE))) + return TRUE; +#else + if (((fmt1 == FMT_U16_NE) && (fmt2 == FMT_U16_LE)) || ((fmt1 == FMT_S16_NE) && (fmt2 == FMT_S16_LE))) + return TRUE; +#endif + + return FALSE; /* no match */ +} + +gchar * +format_name(AFormat fmt) +{ + switch (fmt) + { + case FMT_U8: + return "FMT_U8"; + case FMT_S8: + return "FMT_S8"; + case FMT_U16_LE: + return "FMT_U16_LE"; + case FMT_U16_BE: + return "FMT_U16_BE"; + case FMT_U16_NE: + return "FMT_U16_NE"; + case FMT_S16_LE: + return "FMT_S16_LE"; + case FMT_S16_BE: + return "FMT_S16_BE"; + case FMT_S16_NE: + return "FMT_S16_NE"; + } + return "UNKNOWN"; +} + +void +format_copy(format_t * dest, format_t * src) +{ + memcpy(dest, src, sizeof(format_t)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/format.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,54 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +/* + * Verify format and setup a struct format_t + * Returns -1 if an illegal format has been specified + */ + +#ifndef __FORMAT_H__ +#define __FORMAT_H__ + +#include "crossfade.h" + +typedef struct +{ + AFormat fmt; + gint rate; + gint nch; + + gint bps; /* format is valid if (bps > 0) */ + gboolean is_8bit; + gboolean is_swapped; + gboolean is_unsigned; +} +format_t; + +gboolean setup_format(AFormat fmt, gint rate, gint nch, format_t * format); + +gboolean format_match(AFormat fmt1, AFormat fmt2); +gchar *format_name(AFormat fmt); + +void format_copy(format_t * dest, format_t * src); + +#endif /* _FORMAT_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/interface-2.0.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,2944 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> + +#include "callbacks.h" +#include "interface-2.0.h" +#include "support-2.0.h" + +#define GLADE_HOOKUP_OBJECT(component,widget,name) \ + g_object_set_data_full (G_OBJECT (component), name, \ + gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref) + +#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \ + g_object_set_data (G_OBJECT (component), name, widget) + +GtkWidget* +create_config_win (void) +{ + GtkWidget *config_win; + GtkWidget *config_vbox; + GtkWidget *config_notebook; + GtkWidget *config_output_page; + GtkWidget *output_options_hbox; + GtkWidget *output_method_frame; + GtkWidget *output_method_vbox; + GtkWidget *output_oss_radio; + GSList *output_oss_radio_group = NULL; + GtkWidget *output_plugin_radio; + GtkWidget *output_none_radio; + GtkWidget *output_method_frame_label; + GtkWidget *output_resampling_frame; + GtkWidget *output_resampling_table; + GtkWidget *resampling_rate_optionmenu; + GtkWidget *menu1; + GtkWidget *resampling_rate_optionmenu_dummy; + GtkWidget *resampling_quality_optionmenu; + GtkWidget *menu2; + GtkWidget *resampling_quality_optionmenu_dummy; + GtkWidget *resampling_rate_hbox; + GtkWidget *resampling_rate_label; + GtkWidget *resampling_quality_hbox; + GtkWidget *resampling_quality_label; + GtkWidget *output_resampling_frame_label; + GtkWidget *output_notebook; + GtkWidget *output_oss_page; + GtkWidget *output_oss_notebook; + GtkWidget *oss_device_page; + GtkWidget *oss_adevice_frame; + GtkWidget *oss_adevice_vbox; + GtkWidget *oss_adevice_hbox; + GtkWidget *oss_adevice_optionmenu; + GtkWidget *oss_adevice_optionmenu_menu; + GtkWidget *oss_adevice_optionmenu_dummy; + GtkWidget *oss_adevice_alt_hbox; + GtkWidget *oss_adevice_alt_check; + GtkWidget *oss_adevice_alt_entry; + GtkWidget *label3; + GtkWidget *oss_mdevice_frame; + GtkWidget *oss_mdevice_vbox; + GtkWidget *oss_mdevice_hbox; + GtkWidget *oss_mdevice_optionmenu; + GtkWidget *oss_mdevice_optionmenu_menu; + GtkWidget *oss_mdevice_optionmenu_dummy; + GtkWidget *oss_mdevice_alt_hbox; + GtkWidget *oss_mdevice_alt_check; + GtkWidget *oss_mdevice_alt_entry; + GtkWidget *label4; + GtkWidget *oss_device_label; + GtkWidget *oss_buffer_page; + GtkWidget *oss_buffer_frame; + GtkWidget *oss_buffer_vbox; + GtkWidget *ossbuf_buffer_hbox; + GtkWidget *ossbuf_buffer_label; + GtkObject *ossbuf_buffer_spin_adj; + GtkWidget *ossbuf_buffer_spin; + GtkWidget *ossbuf_preload_hbox; + GtkWidget *ossbuf_preload_label; + GtkObject *ossbuf_preload_spin_adj; + GtkWidget *ossbuf_preload_spin; + GtkWidget *label5; + GtkWidget *oss_hwbuf_frame; + GtkWidget *oss_hwbuf_vbox; + GtkWidget *osshwb_maxbuf_check; + GtkWidget *osshwb_fragments_hbox; + GtkWidget *osshwb_fragments_label; + GtkObject *osshwb_fragments_spin_adj; + GtkWidget *osshwb_fragments_spin; + GtkWidget *osshwb_fragsize_hbox; + GtkWidget *osshwb_fragsize_label; + GtkObject *osshwb_fragsize_spin_adj; + GtkWidget *osshwb_fragsize_spin; + GtkWidget *label6; + GtkWidget *oss_buffer_label; + GtkWidget *oss_mixer_page; + GtkWidget *oss_mixer_frame; + GtkWidget *oss_mixer_vbox; + GtkWidget *ossmixer_pcm_check; + GtkWidget *label7; + GtkWidget *oss_mixer_label; + GtkWidget *output_plugin_label; + GtkWidget *output_plugin_page; + GtkWidget *op_plugin_frame; + GtkWidget *op_plugin_vbox; + GtkWidget *op_plugin_optionmenu; + GtkWidget *op_plugin_optionmenu_menu; + GtkWidget *op_plugin_optionmenu_dummy; + GtkWidget *op_plugin_buttonbox; + GtkWidget *op_configure_button; + GtkWidget *op_about_button; + GtkWidget *label8; + GtkWidget *op_options_frame; + GtkWidget *op_options_vbox; + GtkWidget *op_throttle_check; + GtkWidget *op_maxblock_hbox; + GtkWidget *op_maxblock_check; + GtkObject *op_maxblock_spin_adj; + GtkWidget *op_maxblock_spin; + GtkWidget *op_forcereopen_check; + GtkWidget *label9; + GtkWidget *output_oss_label; + GtkWidget *empty_notebook_page; + GtkWidget *output_null_label; + GtkWidget *output_help_label; + GtkWidget *config_devices_label; + GtkWidget *config_effects_page; + GtkWidget *ep_plugin_frame; + GtkWidget *ep_plugin_vbox; + GtkWidget *ep_plugin_optionmenu; + GtkWidget *ep_plugin_optionmenu_menu; + GtkWidget *ep_plugin_optionmenu_dummy; + GtkWidget *ep_plugin_buttonbox; + GtkWidget *ep_configure_button; + GtkWidget *ep_about_button; + GtkWidget *ep_enable_check; + GtkWidget *ep_plugin_frame_label; + GtkWidget *effects_volnorm_frame; + GtkWidget *effects_volnorm_table; + GtkWidget *volnorm_quantaudio_check; + GtkObject *volnorm_target_spin_adj; + GtkWidget *volnorm_target_spin; + GtkWidget *volnorm_target_hbox; + GtkWidget *volnorm_target_label; + GtkWidget *volnorm_enable_check; + GtkWidget *volnorm_rva2_check; + GtkWidget *label11; + GtkWidget *effects_help_label; + GtkWidget *config_effects_label; + GtkWidget *config_crossfader_page; + GtkWidget *xf_bufsize_hbox; + GtkWidget *xf_bufsize_label; + GtkObject *xf_buffer_spin_adj; + GtkWidget *xf_buffer_spin; + GtkWidget *xf_autobuf_check; + GtkWidget *xf_config_hbox; + GtkWidget *xf_config_label; + GtkWidget *xf_config_optionmenu; + GtkWidget *xf_config_optionmenu_menu; + GtkWidget *xf_config_optionmenu_dummy; + GtkWidget *xf_type_hbox; + GtkWidget *xf_type_label; + GtkWidget *xf_type_optionmenu; + GtkWidget *xf_type_optionmenu_menu; + GtkWidget *xf_type_optionmenu_dummy; + GtkWidget *xf_type_notebook; + GtkWidget *xft_reopen_label; + GtkWidget *xft_flush_page; + GtkWidget *xftf_pause_frame; + GtkWidget *xftf_pause_table; + GtkWidget *xftfp_length_label; + GtkObject *xftfp_length_spin_adj; + GtkWidget *xftfp_length_spin; + GtkWidget *xftfp_enable_check; + GtkWidget *label12; + GtkWidget *xftf_fadein_frame; + GtkWidget *xftf_fadein_table; + GtkWidget *xftffi_length_hbox; + GtkWidget *xftffi_length_label; + GtkWidget *xftffi_volume_hbox; + GtkWidget *xftffi_volume_label; + GtkObject *xftffi_length_spin_adj; + GtkWidget *xftffi_length_spin; + GtkWidget *xftffi_enable_check; + GtkObject *xftffi_volume_spin_adj; + GtkWidget *xftffi_volume_spin; + GtkWidget *label13; + GtkWidget *xft_flush_label; + GtkWidget *xft_none_label; + GtkWidget *xft_pause_page; + GtkWidget *xf_pause_frame; + GtkWidget *xf_pause_table; + GtkWidget *pause_length_hbox; + GtkWidget *pause_length_label; + GtkObject *pause_length_spin_adj; + GtkWidget *pause_length_spin; + GtkWidget *label14; + GtkWidget *xft_pause_label; + GtkWidget *xft_simplexf_page; + GtkWidget *xf_simple_frame; + GtkWidget *xf_simple_table; + GtkWidget *simple_length_hbox; + GtkWidget *simple_length_label; + GtkObject *simple_length_spin_adj; + GtkWidget *simple_length_spin; + GtkWidget *label15; + GtkWidget *xft_simplexf_label; + GtkWidget *xft_advancedxf_page; + GtkWidget *xf_fadeout_frame; + GtkWidget *xf_fadeout_table; + GtkWidget *fadeout_length_hbox; + GtkWidget *fadeout_length_label; + GtkObject *fadeout_length_spin_adj; + GtkWidget *fadeout_length_spin; + GtkWidget *fadeout_volume_hbox; + GtkWidget *fadeout_volume_label; + GtkObject *fadeout_volume_spin_adj; + GtkWidget *fadeout_volume_spin; + GtkWidget *fadeout_options_hbox; + GtkWidget *fadeout_enable_check; + GtkWidget *label16; + GtkWidget *xf_offset_frame; + GtkWidget *xf_offset_table; + GtkWidget *xfofs_custom_hbox; + GtkWidget *xfofs_custom_radiobutton; + GSList *xfofs_custom_radiobutton_group = NULL; + GtkObject *xfofs_custom_spin_adj; + GtkWidget *xfofs_custom_spin; + GtkWidget *xfofs_none_radiobutton; + GtkWidget *xfofs_lockout_radiobutton; + GtkWidget *xfofs_lockin_radiobutton; + GtkWidget *label17; + GtkWidget *xf_fadein_frame; + GtkWidget *xf_fadein_table; + GtkWidget *fadein_length_hbox; + GtkWidget *fadein_length_label; + GtkWidget *fadein_volume_hbox; + GtkWidget *fadein_volume_label; + GtkObject *fadein_length_spin_adj; + GtkWidget *fadein_length_spin; + GtkWidget *fadein_enable_check; + GtkWidget *fadein_lock_check; + GtkObject *fadein_volume_spin_adj; + GtkWidget *fadein_volume_spin; + GtkWidget *label18; + GtkWidget *xft_advancedxf_label; + GtkWidget *xft_fadein_page; + GtkWidget *xftfi_fadein_frame; + GtkWidget *xftfi_fadein_table; + GtkWidget *xftfi_length_hbox; + GtkWidget *xftfi_length_label; + GtkWidget *xftfi_volume_hbox; + GtkWidget *xftfi_volume_label; + GtkObject *xftfi_length_spin_adj; + GtkWidget *xftfi_length_spin; + GtkObject *xftfi_volume_spin_adj; + GtkWidget *xftfi_volume_spin; + GtkWidget *label19; + GtkWidget *xft_fadein_label; + GtkWidget *xft_fadeout_page; + GtkWidget *xftfo_fadeout_frame; + GtkWidget *xftfo_fadeout_table; + GtkWidget *xftfo_length_hbox; + GtkWidget *xftfo_length_label; + GtkObject *xftfo_length_spin_adj; + GtkWidget *xftfo_length_spin; + GtkWidget *xftfo_volume_hbox; + GtkWidget *xftfo_volume_label; + GtkObject *xftfo_volume_spin_adj; + GtkWidget *xftfo_volume_spin; + GtkWidget *label20; + GtkWidget *xftfo_silence_frame; + GtkWidget *xftfo_silence_table; + GtkWidget *xftfo_silence_hbox; + GtkWidget *xftfo_silence_label; + GtkObject *xftfo_silence_spin_adj; + GtkWidget *xftfo_silence_spin; + GtkWidget *label21; + GtkWidget *xft_fadeout_label; + GtkWidget *xft_pause_none_label; + GtkWidget *xft_pause_adv_page; + GtkWidget *xft_fadeoutin_frame; + GtkWidget *xft_fadeoutin_table; + GtkObject *xftfoi_fadeout_spin_adj; + GtkWidget *xftfoi_fadeout_spin; + GtkObject *xftfoi_silence_spin_adj; + GtkWidget *xftfoi_silence_spin; + GtkObject *xftfoi_fadein_spin_adj; + GtkWidget *xftfoi_fadein_spin; + GtkWidget *xftfoi_fadein_hbox; + GtkWidget *xftfoi_fadein_label; + GtkWidget *xftfoi_silence_hbox; + GtkWidget *xftfoi_silence_label; + GtkWidget *xftfoi_fadeout_hbox; + GtkWidget *xftfoi_fadeout_label; + GtkWidget *label22; + GtkWidget *xft_pause_adv_label; + GtkWidget *config_crossfade_label; + GtkWidget *config_gapkiller_page; + GtkWidget *gap_leading_frame; + GtkWidget *gap_leading_table; + GtkObject *lgap_length_spin_adj; + GtkWidget *lgap_length_spin; + GtkObject *lgap_level_spin_adj; + GtkWidget *lgap_level_spin; + GtkWidget *lgap_length_hbox; + GtkWidget *lgap_length_label; + GtkWidget *lgap_level_hbox; + GtkWidget *lgap_level_label; + GtkWidget *lgap_enable_check; + GtkWidget *label23; + GtkWidget *gap_trailing_frame; + GtkWidget *gap_trailing_table; + GtkWidget *tgap_length_hbox; + GtkWidget *tgap_length_label; + GtkWidget *tgap_level_hbox; + GtkWidget *tgap_level_label; + GtkObject *tgap_length_spin_adj; + GtkWidget *tgap_length_spin; + GtkObject *tgap_level_spin_adj; + GtkWidget *tgap_level_spin; + GtkWidget *tgap_lock_check; + GtkWidget *tgap_enable_check; + GtkWidget *label24; + GtkWidget *gap_advanced_frame; + GtkWidget *gap_advanced_vbox; + GtkWidget *gadv_crossing_check; + GtkWidget *label25; + GtkWidget *config_gapkiller_label; + GtkWidget *config_misc_page; + GtkWidget *misc_debug_frame; + GtkWidget *misc_debug_vbox; + GtkWidget *debug_stderr_check; + GtkWidget *debug_monitor_check; + GtkWidget *label26; + GtkWidget *misc_mixopt_frame; + GtkWidget *misc_mixopt_vbox; + GtkWidget *mixopt_enable_check; + GtkWidget *mixopt_reverse_check; + GtkWidget *mixopt_software_check; + GtkWidget *label27; + GtkWidget *misc_other_frame; + GtkWidget *misc_other_vbox; + GtkWidget *moth_songchange_hbox; + GtkWidget *moth_songchange_label; + GtkObject *moth_songchange_spin_adj; + GtkWidget *moth_songchange_spin; + GtkWidget *moth_preload_hbox; + GtkWidget *moth_preload_label; + GtkObject *moth_preload_spin_adj; + GtkWidget *moth_preload_spin; + GtkWidget *moth_noxf_hbox; + GtkWidget *moth_noxf_label; + GtkWidget *noxf_album_check; + GtkWidget *noxf_samefile_check; + GtkWidget *moth_httpworkaround_check; + GtkWidget *moth_opmaxused_hbox; + GtkWidget *moth_opmaxused_check; + GtkObject *moth_opmaxused_spin_adj; + GtkWidget *moth_opmaxused_spin; + GtkWidget *moth_quantaudio_check; + GtkWidget *moth_outputkeepopened_check; + GtkWidget *label28; + GtkWidget *config_misc_label; + GtkWidget *config_presets_page; + GtkWidget *presets_name_hbox; + GtkWidget *presets_name_entry; + GtkWidget *presets_delete_button; + GtkWidget *presets_new_button; + GtkWidget *presets_list_scrolledwindow; + GtkWidget *presets_list_list; + GtkWidget *presets_list_bbox; + GtkWidget *presets_load_button; + GtkWidget *presets_save_button; + GtkWidget *config_presets_label; + GtkWidget *config_bbox; + GtkWidget *config_ok; + GtkWidget *config_cancel; + GtkWidget *config_apply; + GtkTooltips *tooltips; + + tooltips = gtk_tooltips_new (); + + config_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (config_win), "Crossfade Configuration"); + + config_vbox = gtk_vbox_new (FALSE, 10); + gtk_widget_show (config_vbox); + gtk_container_add (GTK_CONTAINER (config_win), config_vbox); + gtk_container_set_border_width (GTK_CONTAINER (config_vbox), 10); + + config_notebook = gtk_notebook_new (); + gtk_widget_show (config_notebook); + gtk_box_pack_start (GTK_BOX (config_vbox), config_notebook, TRUE, TRUE, 0); + + config_output_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (config_output_page); + gtk_container_add (GTK_CONTAINER (config_notebook), config_output_page); + gtk_container_set_border_width (GTK_CONTAINER (config_output_page), 5); + + output_options_hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (output_options_hbox); + gtk_box_pack_start (GTK_BOX (config_output_page), output_options_hbox, FALSE, TRUE, 0); + + output_method_frame = gtk_frame_new (NULL); + gtk_widget_show (output_method_frame); + gtk_box_pack_start (GTK_BOX (output_options_hbox), output_method_frame, TRUE, TRUE, 0); + + output_method_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (output_method_vbox); + gtk_container_add (GTK_CONTAINER (output_method_frame), output_method_vbox); + gtk_container_set_border_width (GTK_CONTAINER (output_method_vbox), 5); + + output_oss_radio = gtk_radio_button_new_with_mnemonic (NULL, "Builtin OSS driver"); + gtk_widget_show (output_oss_radio); + gtk_box_pack_start (GTK_BOX (output_method_vbox), output_oss_radio, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, output_oss_radio, "Use XMMS-crossfade's builtin OSS driver for output.", NULL); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (output_oss_radio), output_oss_radio_group); + output_oss_radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (output_oss_radio)); + + output_plugin_radio = gtk_radio_button_new_with_mnemonic (NULL, "Output plugin"); + gtk_widget_show (output_plugin_radio); + gtk_box_pack_start (GTK_BOX (output_method_vbox), output_plugin_radio, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, output_plugin_radio, "Use an existing plugin for output.", NULL); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (output_plugin_radio), output_oss_radio_group); + output_oss_radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (output_plugin_radio)); + + output_none_radio = gtk_radio_button_new_with_mnemonic (NULL, "None [not implemented]"); + gtk_box_pack_start (GTK_BOX (output_method_vbox), output_none_radio, FALSE, FALSE, 0); + gtk_widget_set_sensitive (output_none_radio, FALSE); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (output_none_radio), output_oss_radio_group); + output_oss_radio_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (output_none_radio)); + + output_method_frame_label = gtk_label_new ("Output method"); + gtk_widget_show (output_method_frame_label); + gtk_frame_set_label_widget (GTK_FRAME (output_method_frame), output_method_frame_label); + + output_resampling_frame = gtk_frame_new (NULL); + gtk_widget_show (output_resampling_frame); + gtk_box_pack_start (GTK_BOX (output_options_hbox), output_resampling_frame, FALSE, FALSE, 0); + + output_resampling_table = gtk_table_new (2, 2, FALSE); + gtk_widget_show (output_resampling_table); + gtk_container_add (GTK_CONTAINER (output_resampling_frame), output_resampling_table); + gtk_container_set_border_width (GTK_CONTAINER (output_resampling_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (output_resampling_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (output_resampling_table), 2); + + resampling_rate_optionmenu = gtk_option_menu_new (); + gtk_widget_show (resampling_rate_optionmenu); + gtk_table_attach (GTK_TABLE (output_resampling_table), resampling_rate_optionmenu, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, resampling_rate_optionmenu, "Set sampling rate for mixing buffer.\nDefault: 44100", NULL); + + menu1 = gtk_menu_new (); + + resampling_rate_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (resampling_rate_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (menu1), resampling_rate_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (resampling_rate_optionmenu), menu1); + + resampling_quality_optionmenu = gtk_option_menu_new (); + gtk_widget_show (resampling_quality_optionmenu); + gtk_table_attach (GTK_TABLE (output_resampling_table), resampling_quality_optionmenu, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, resampling_quality_optionmenu, "Set resampling algorithm.", NULL); + + menu2 = gtk_menu_new (); + + resampling_quality_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (resampling_quality_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (menu2), resampling_quality_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (resampling_quality_optionmenu), menu2); + + resampling_rate_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (resampling_rate_hbox); + gtk_table_attach (GTK_TABLE (output_resampling_table), resampling_rate_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + resampling_rate_label = gtk_label_new ("Rate:"); + gtk_widget_show (resampling_rate_label); + gtk_box_pack_end (GTK_BOX (resampling_rate_hbox), resampling_rate_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (resampling_rate_label), GTK_JUSTIFY_CENTER); + + resampling_quality_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (resampling_quality_hbox); + gtk_table_attach (GTK_TABLE (output_resampling_table), resampling_quality_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + resampling_quality_label = gtk_label_new ("Quality:"); + gtk_widget_show (resampling_quality_label); + gtk_box_pack_end (GTK_BOX (resampling_quality_hbox), resampling_quality_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (resampling_quality_label), GTK_JUSTIFY_CENTER); + + output_resampling_frame_label = gtk_label_new ("Resampling"); + gtk_widget_show (output_resampling_frame_label); + gtk_frame_set_label_widget (GTK_FRAME (output_resampling_frame), output_resampling_frame_label); + + output_notebook = gtk_notebook_new (); + gtk_widget_show (output_notebook); + gtk_box_pack_start (GTK_BOX (config_output_page), output_notebook, FALSE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (output_notebook, GTK_CAN_FOCUS); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (output_notebook), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (output_notebook), FALSE); + + output_oss_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (output_oss_page); + gtk_container_add (GTK_CONTAINER (output_notebook), output_oss_page); + + output_oss_notebook = gtk_notebook_new (); + gtk_widget_show (output_oss_notebook); + gtk_box_pack_start (GTK_BOX (output_oss_page), output_oss_notebook, FALSE, FALSE, 0); + + oss_device_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (oss_device_page); + gtk_container_add (GTK_CONTAINER (output_oss_notebook), oss_device_page); + gtk_container_set_border_width (GTK_CONTAINER (oss_device_page), 5); + + oss_adevice_frame = gtk_frame_new (NULL); + gtk_widget_show (oss_adevice_frame); + gtk_box_pack_start (GTK_BOX (oss_device_page), oss_adevice_frame, FALSE, FALSE, 0); + + oss_adevice_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (oss_adevice_vbox); + gtk_container_add (GTK_CONTAINER (oss_adevice_frame), oss_adevice_vbox); + gtk_container_set_border_width (GTK_CONTAINER (oss_adevice_vbox), 5); + + oss_adevice_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (oss_adevice_hbox); + gtk_box_pack_start (GTK_BOX (oss_adevice_vbox), oss_adevice_hbox, TRUE, TRUE, 0); + + oss_adevice_optionmenu = gtk_option_menu_new (); + gtk_widget_show (oss_adevice_optionmenu); + gtk_box_pack_start (GTK_BOX (oss_adevice_hbox), oss_adevice_optionmenu, TRUE, TRUE, 0); + + oss_adevice_optionmenu_menu = gtk_menu_new (); + + oss_adevice_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (oss_adevice_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (oss_adevice_optionmenu_menu), oss_adevice_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (oss_adevice_optionmenu), oss_adevice_optionmenu_menu); + + oss_adevice_alt_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (oss_adevice_alt_hbox); + gtk_box_pack_start (GTK_BOX (oss_adevice_vbox), oss_adevice_alt_hbox, TRUE, TRUE, 0); + + oss_adevice_alt_check = gtk_check_button_new_with_mnemonic ("Use alternate device:"); + gtk_widget_show (oss_adevice_alt_check); + gtk_box_pack_start (GTK_BOX (oss_adevice_alt_hbox), oss_adevice_alt_check, FALSE, FALSE, 0); + + oss_adevice_alt_entry = gtk_entry_new (); + gtk_widget_show (oss_adevice_alt_entry); + gtk_box_pack_start (GTK_BOX (oss_adevice_alt_hbox), oss_adevice_alt_entry, TRUE, TRUE, 0); + + label3 = gtk_label_new ("Audio device"); + gtk_widget_show (label3); + gtk_frame_set_label_widget (GTK_FRAME (oss_adevice_frame), label3); + + oss_mdevice_frame = gtk_frame_new (NULL); + gtk_widget_show (oss_mdevice_frame); + gtk_box_pack_start (GTK_BOX (oss_device_page), oss_mdevice_frame, FALSE, FALSE, 0); + + oss_mdevice_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (oss_mdevice_vbox); + gtk_container_add (GTK_CONTAINER (oss_mdevice_frame), oss_mdevice_vbox); + gtk_container_set_border_width (GTK_CONTAINER (oss_mdevice_vbox), 5); + + oss_mdevice_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (oss_mdevice_hbox); + gtk_box_pack_start (GTK_BOX (oss_mdevice_vbox), oss_mdevice_hbox, TRUE, TRUE, 0); + + oss_mdevice_optionmenu = gtk_option_menu_new (); + gtk_widget_show (oss_mdevice_optionmenu); + gtk_box_pack_start (GTK_BOX (oss_mdevice_hbox), oss_mdevice_optionmenu, TRUE, TRUE, 0); + + oss_mdevice_optionmenu_menu = gtk_menu_new (); + + oss_mdevice_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (oss_mdevice_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (oss_mdevice_optionmenu_menu), oss_mdevice_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (oss_mdevice_optionmenu), oss_mdevice_optionmenu_menu); + + oss_mdevice_alt_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (oss_mdevice_alt_hbox); + gtk_box_pack_start (GTK_BOX (oss_mdevice_vbox), oss_mdevice_alt_hbox, FALSE, FALSE, 0); + + oss_mdevice_alt_check = gtk_check_button_new_with_mnemonic ("Use alternate device:"); + gtk_widget_show (oss_mdevice_alt_check); + gtk_box_pack_start (GTK_BOX (oss_mdevice_alt_hbox), oss_mdevice_alt_check, FALSE, FALSE, 0); + + oss_mdevice_alt_entry = gtk_entry_new (); + gtk_widget_show (oss_mdevice_alt_entry); + gtk_box_pack_start (GTK_BOX (oss_mdevice_alt_hbox), oss_mdevice_alt_entry, TRUE, TRUE, 0); + + label4 = gtk_label_new ("Mixer device"); + gtk_widget_show (label4); + gtk_frame_set_label_widget (GTK_FRAME (oss_mdevice_frame), label4); + + oss_device_label = gtk_label_new ("Device"); + gtk_widget_show (oss_device_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (output_oss_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (output_oss_notebook), 0), oss_device_label); + gtk_label_set_justify (GTK_LABEL (oss_device_label), GTK_JUSTIFY_CENTER); + + oss_buffer_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (oss_buffer_page); + gtk_container_add (GTK_CONTAINER (output_oss_notebook), oss_buffer_page); + gtk_container_set_border_width (GTK_CONTAINER (oss_buffer_page), 5); + + oss_buffer_frame = gtk_frame_new (NULL); + gtk_widget_show (oss_buffer_frame); + gtk_box_pack_start (GTK_BOX (oss_buffer_page), oss_buffer_frame, FALSE, FALSE, 0); + + oss_buffer_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (oss_buffer_vbox); + gtk_container_add (GTK_CONTAINER (oss_buffer_frame), oss_buffer_vbox); + gtk_container_set_border_width (GTK_CONTAINER (oss_buffer_vbox), 5); + + ossbuf_buffer_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (ossbuf_buffer_hbox); + gtk_box_pack_start (GTK_BOX (oss_buffer_vbox), ossbuf_buffer_hbox, FALSE, FALSE, 0); + + ossbuf_buffer_label = gtk_label_new ("Buffer size (ms):"); + gtk_widget_show (ossbuf_buffer_label); + gtk_box_pack_start (GTK_BOX (ossbuf_buffer_hbox), ossbuf_buffer_label, FALSE, FALSE, 0); + + ossbuf_buffer_spin_adj = gtk_adjustment_new (0, 0, 60000, 10, 100, 10); + ossbuf_buffer_spin = gtk_spin_button_new (GTK_ADJUSTMENT (ossbuf_buffer_spin_adj), 0, 0); + gtk_widget_show (ossbuf_buffer_spin); + gtk_box_pack_start (GTK_BOX (ossbuf_buffer_hbox), ossbuf_buffer_spin, TRUE, TRUE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (ossbuf_buffer_spin), TRUE); + + ossbuf_preload_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (ossbuf_preload_hbox); + gtk_box_pack_start (GTK_BOX (oss_buffer_vbox), ossbuf_preload_hbox, TRUE, TRUE, 0); + + ossbuf_preload_label = gtk_label_new ("Preload size (ms):"); + gtk_widget_show (ossbuf_preload_label); + gtk_box_pack_start (GTK_BOX (ossbuf_preload_hbox), ossbuf_preload_label, FALSE, FALSE, 0); + + ossbuf_preload_spin_adj = gtk_adjustment_new (1500, 0, 60000, 10, 100, 10); + ossbuf_preload_spin = gtk_spin_button_new (GTK_ADJUSTMENT (ossbuf_preload_spin_adj), 0, 0); + gtk_widget_show (ossbuf_preload_spin); + gtk_box_pack_start (GTK_BOX (ossbuf_preload_hbox), ossbuf_preload_spin, TRUE, TRUE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (ossbuf_preload_spin), TRUE); + + label5 = gtk_label_new ("Software buffer"); + gtk_widget_show (label5); + gtk_frame_set_label_widget (GTK_FRAME (oss_buffer_frame), label5); + + oss_hwbuf_frame = gtk_frame_new (NULL); + gtk_widget_show (oss_hwbuf_frame); + gtk_box_pack_start (GTK_BOX (oss_buffer_page), oss_hwbuf_frame, TRUE, TRUE, 0); + + oss_hwbuf_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (oss_hwbuf_vbox); + gtk_container_add (GTK_CONTAINER (oss_hwbuf_frame), oss_hwbuf_vbox); + gtk_container_set_border_width (GTK_CONTAINER (oss_hwbuf_vbox), 5); + + osshwb_maxbuf_check = gtk_check_button_new_with_mnemonic ("Maximum device buffer size"); + gtk_widget_show (osshwb_maxbuf_check); + gtk_box_pack_start (GTK_BOX (oss_hwbuf_vbox), osshwb_maxbuf_check, FALSE, FALSE, 0); + + osshwb_fragments_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (osshwb_fragments_hbox); + gtk_box_pack_start (GTK_BOX (oss_hwbuf_vbox), osshwb_fragments_hbox, FALSE, FALSE, 0); + + osshwb_fragments_label = gtk_label_new ("Number of Fragments:"); + gtk_widget_show (osshwb_fragments_label); + gtk_box_pack_start (GTK_BOX (osshwb_fragments_hbox), osshwb_fragments_label, FALSE, FALSE, 0); + + osshwb_fragments_spin_adj = gtk_adjustment_new (50, 2, 65535, 1, 10, 10); + osshwb_fragments_spin = gtk_spin_button_new (GTK_ADJUSTMENT (osshwb_fragments_spin_adj), 0, 0); + gtk_widget_show (osshwb_fragments_spin); + gtk_box_pack_start (GTK_BOX (osshwb_fragments_hbox), osshwb_fragments_spin, TRUE, TRUE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (osshwb_fragments_spin), TRUE); + + osshwb_fragsize_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (osshwb_fragsize_hbox); + gtk_box_pack_start (GTK_BOX (oss_hwbuf_vbox), osshwb_fragsize_hbox, TRUE, TRUE, 0); + + osshwb_fragsize_label = gtk_label_new ("Fragment size (2^x bytes):"); + gtk_widget_show (osshwb_fragsize_label); + gtk_box_pack_start (GTK_BOX (osshwb_fragsize_hbox), osshwb_fragsize_label, FALSE, FALSE, 0); + + osshwb_fragsize_spin_adj = gtk_adjustment_new (11, 4, 16, 1, 10, 10); + osshwb_fragsize_spin = gtk_spin_button_new (GTK_ADJUSTMENT (osshwb_fragsize_spin_adj), 0, 0); + gtk_widget_show (osshwb_fragsize_spin); + gtk_box_pack_start (GTK_BOX (osshwb_fragsize_hbox), osshwb_fragsize_spin, TRUE, TRUE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (osshwb_fragsize_spin), TRUE); + + label6 = gtk_label_new ("Hardware device buffer"); + gtk_widget_show (label6); + gtk_frame_set_label_widget (GTK_FRAME (oss_hwbuf_frame), label6); + + oss_buffer_label = gtk_label_new ("Buffer"); + gtk_widget_show (oss_buffer_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (output_oss_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (output_oss_notebook), 1), oss_buffer_label); + gtk_label_set_justify (GTK_LABEL (oss_buffer_label), GTK_JUSTIFY_CENTER); + + oss_mixer_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (oss_mixer_page); + gtk_container_add (GTK_CONTAINER (output_oss_notebook), oss_mixer_page); + gtk_container_set_border_width (GTK_CONTAINER (oss_mixer_page), 5); + + oss_mixer_frame = gtk_frame_new (NULL); + gtk_widget_show (oss_mixer_frame); + gtk_box_pack_start (GTK_BOX (oss_mixer_page), oss_mixer_frame, FALSE, FALSE, 0); + + oss_mixer_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (oss_mixer_vbox); + gtk_container_add (GTK_CONTAINER (oss_mixer_frame), oss_mixer_vbox); + gtk_container_set_border_width (GTK_CONTAINER (oss_mixer_vbox), 5); + + ossmixer_pcm_check = gtk_check_button_new_with_mnemonic ("Volume controls Master not PCM"); + gtk_widget_show (ossmixer_pcm_check); + gtk_box_pack_start (GTK_BOX (oss_mixer_vbox), ossmixer_pcm_check, FALSE, FALSE, 0); + + label7 = gtk_label_new ("Mixer options"); + gtk_widget_show (label7); + gtk_frame_set_label_widget (GTK_FRAME (oss_mixer_frame), label7); + + oss_mixer_label = gtk_label_new ("Mixer"); + gtk_widget_show (oss_mixer_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (output_oss_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (output_oss_notebook), 2), oss_mixer_label); + gtk_label_set_justify (GTK_LABEL (oss_mixer_label), GTK_JUSTIFY_CENTER); + + output_plugin_label = gtk_label_new ("plugin"); + gtk_widget_show (output_plugin_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (output_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (output_notebook), 0), output_plugin_label); + + output_plugin_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (output_plugin_page); + gtk_container_add (GTK_CONTAINER (output_notebook), output_plugin_page); + + op_plugin_frame = gtk_frame_new (NULL); + gtk_widget_show (op_plugin_frame); + gtk_box_pack_start (GTK_BOX (output_plugin_page), op_plugin_frame, FALSE, TRUE, 0); + + op_plugin_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (op_plugin_vbox); + gtk_container_add (GTK_CONTAINER (op_plugin_frame), op_plugin_vbox); + gtk_container_set_border_width (GTK_CONTAINER (op_plugin_vbox), 5); + + op_plugin_optionmenu = gtk_option_menu_new (); + gtk_widget_show (op_plugin_optionmenu); + gtk_box_pack_start (GTK_BOX (op_plugin_vbox), op_plugin_optionmenu, FALSE, FALSE, 0); + + op_plugin_optionmenu_menu = gtk_menu_new (); + + op_plugin_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (op_plugin_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (op_plugin_optionmenu_menu), op_plugin_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (op_plugin_optionmenu), op_plugin_optionmenu_menu); + + op_plugin_buttonbox = gtk_hbutton_box_new (); + gtk_widget_show (op_plugin_buttonbox); + gtk_box_pack_start (GTK_BOX (op_plugin_vbox), op_plugin_buttonbox, FALSE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (op_plugin_buttonbox), GTK_BUTTONBOX_START); + gtk_box_set_spacing (GTK_BOX (op_plugin_buttonbox), 5); + + op_configure_button = gtk_button_new_with_mnemonic ("Configure"); + gtk_widget_show (op_configure_button); + gtk_container_add (GTK_CONTAINER (op_plugin_buttonbox), op_configure_button); + GTK_WIDGET_SET_FLAGS (op_configure_button, GTK_CAN_DEFAULT); + + op_about_button = gtk_button_new_with_mnemonic ("About"); + gtk_widget_show (op_about_button); + gtk_container_add (GTK_CONTAINER (op_plugin_buttonbox), op_about_button); + GTK_WIDGET_SET_FLAGS (op_about_button, GTK_CAN_DEFAULT); + + label8 = gtk_label_new ("Output plugin"); + gtk_widget_show (label8); + gtk_frame_set_label_widget (GTK_FRAME (op_plugin_frame), label8); + + op_options_frame = gtk_frame_new (NULL); + gtk_widget_show (op_options_frame); + gtk_box_pack_start (GTK_BOX (output_plugin_page), op_options_frame, FALSE, TRUE, 0); + + op_options_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (op_options_vbox); + gtk_container_add (GTK_CONTAINER (op_options_frame), op_options_vbox); + gtk_container_set_border_width (GTK_CONTAINER (op_options_vbox), 5); + + op_throttle_check = gtk_check_button_new_with_mnemonic ("Throttle Output"); + gtk_widget_show (op_throttle_check); + gtk_box_pack_start (GTK_BOX (op_options_vbox), op_throttle_check, FALSE, FALSE, 0); + + op_maxblock_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (op_maxblock_hbox); + gtk_box_pack_start (GTK_BOX (op_options_vbox), op_maxblock_hbox, TRUE, TRUE, 0); + + op_maxblock_check = gtk_check_button_new_with_mnemonic ("Max block size (bytes):"); + gtk_widget_show (op_maxblock_check); + gtk_box_pack_start (GTK_BOX (op_maxblock_hbox), op_maxblock_check, FALSE, FALSE, 0); + + op_maxblock_spin_adj = gtk_adjustment_new (1024, 4, 1048580, 4, 16, 10); + op_maxblock_spin = gtk_spin_button_new (GTK_ADJUSTMENT (op_maxblock_spin_adj), 10, 0); + gtk_widget_show (op_maxblock_spin); + gtk_box_pack_start (GTK_BOX (op_maxblock_hbox), op_maxblock_spin, TRUE, TRUE, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (op_maxblock_spin), TRUE); + gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (op_maxblock_spin), TRUE); + + op_forcereopen_check = gtk_check_button_new_with_mnemonic ("Force close/reopen on songchange"); + gtk_widget_show (op_forcereopen_check); + gtk_box_pack_start (GTK_BOX (op_options_vbox), op_forcereopen_check, FALSE, FALSE, 0); + + label9 = gtk_label_new ("Plugin compatibility options"); + gtk_widget_show (label9); + gtk_frame_set_label_widget (GTK_FRAME (op_options_frame), label9); + + output_oss_label = gtk_label_new ("oss"); + gtk_widget_show (output_oss_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (output_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (output_notebook), 1), output_oss_label); + + empty_notebook_page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (empty_notebook_page); + gtk_container_add (GTK_CONTAINER (output_notebook), empty_notebook_page); + + output_null_label = gtk_label_new ("null"); + gtk_widget_show (output_null_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (output_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (output_notebook), 2), output_null_label); + + output_help_label = gtk_label_new ("When modifying the Output Options or the Mixing buffer size, you need to stop/restart playback for the settings to take effect."); + gtk_widget_show (output_help_label); + gtk_box_pack_start (GTK_BOX (config_output_page), output_help_label, TRUE, TRUE, 0); + gtk_label_set_line_wrap (GTK_LABEL (output_help_label), TRUE); + + config_devices_label = gtk_label_new ("Output"); + gtk_widget_show (config_devices_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (config_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (config_notebook), 0), config_devices_label); + gtk_label_set_justify (GTK_LABEL (config_devices_label), GTK_JUSTIFY_CENTER); + + config_effects_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (config_effects_page); + gtk_container_add (GTK_CONTAINER (config_notebook), config_effects_page); + gtk_container_set_border_width (GTK_CONTAINER (config_effects_page), 5); + + ep_plugin_frame = gtk_frame_new (NULL); + gtk_widget_show (ep_plugin_frame); + gtk_box_pack_start (GTK_BOX (config_effects_page), ep_plugin_frame, FALSE, TRUE, 0); + + ep_plugin_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (ep_plugin_vbox); + gtk_container_add (GTK_CONTAINER (ep_plugin_frame), ep_plugin_vbox); + gtk_container_set_border_width (GTK_CONTAINER (ep_plugin_vbox), 5); + + ep_plugin_optionmenu = gtk_option_menu_new (); + gtk_widget_show (ep_plugin_optionmenu); + gtk_box_pack_start (GTK_BOX (ep_plugin_vbox), ep_plugin_optionmenu, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, ep_plugin_optionmenu, "Select an effect plugin. Take care not to use the same plugin as selected in XMMS' configuration dialog.", NULL); + + ep_plugin_optionmenu_menu = gtk_menu_new (); + + ep_plugin_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (ep_plugin_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (ep_plugin_optionmenu_menu), ep_plugin_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (ep_plugin_optionmenu), ep_plugin_optionmenu_menu); + + ep_plugin_buttonbox = gtk_hbutton_box_new (); + gtk_widget_show (ep_plugin_buttonbox); + gtk_box_pack_start (GTK_BOX (ep_plugin_vbox), ep_plugin_buttonbox, FALSE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (ep_plugin_buttonbox), GTK_BUTTONBOX_START); + gtk_box_set_spacing (GTK_BOX (ep_plugin_buttonbox), 5); + + ep_configure_button = gtk_button_new_with_mnemonic ("Configure"); + gtk_widget_show (ep_configure_button); + gtk_container_add (GTK_CONTAINER (ep_plugin_buttonbox), ep_configure_button); + GTK_WIDGET_SET_FLAGS (ep_configure_button, GTK_CAN_DEFAULT); + gtk_tooltips_set_tip (tooltips, ep_configure_button, "Open the configuration dialog of the plugin selected above.", NULL); + + ep_about_button = gtk_button_new_with_mnemonic ("About"); + gtk_widget_show (ep_about_button); + gtk_container_add (GTK_CONTAINER (ep_plugin_buttonbox), ep_about_button); + GTK_WIDGET_SET_FLAGS (ep_about_button, GTK_CAN_DEFAULT); + gtk_tooltips_set_tip (tooltips, ep_about_button, "Open the about dialog of the plugin selected above.", NULL); + + ep_enable_check = gtk_check_button_new_with_mnemonic ("Use plugin"); + gtk_widget_show (ep_enable_check); + gtk_container_add (GTK_CONTAINER (ep_plugin_buttonbox), ep_enable_check); + gtk_tooltips_set_tip (tooltips, ep_enable_check, "Enable the selected plugin. Note that after pressing 'Apply' it may take a while until you can hear the effect, since it is applied before the audio data goes into the buffer.", NULL); + + ep_plugin_frame_label = gtk_label_new ("Pre-mixing effect plugin"); + gtk_widget_show (ep_plugin_frame_label); + gtk_frame_set_label_widget (GTK_FRAME (ep_plugin_frame), ep_plugin_frame_label); + + effects_volnorm_frame = gtk_frame_new (NULL); + gtk_box_pack_start (GTK_BOX (config_effects_page), effects_volnorm_frame, FALSE, FALSE, 0); + + effects_volnorm_table = gtk_table_new (4, 2, FALSE); + gtk_widget_show (effects_volnorm_table); + gtk_container_add (GTK_CONTAINER (effects_volnorm_frame), effects_volnorm_table); + gtk_container_set_border_width (GTK_CONTAINER (effects_volnorm_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (effects_volnorm_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (effects_volnorm_table), 5); + + volnorm_quantaudio_check = gtk_check_button_new_with_mnemonic ("Read Quantaudio comment field"); + gtk_widget_show (volnorm_quantaudio_check); + gtk_table_attach (GTK_TABLE (effects_volnorm_table), volnorm_quantaudio_check, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + volnorm_target_spin_adj = gtk_adjustment_new (8000, 0, 32768, 100, 1000, 10); + volnorm_target_spin = gtk_spin_button_new (GTK_ADJUSTMENT (volnorm_target_spin_adj), 0, 0); + gtk_widget_show (volnorm_target_spin); + gtk_table_attach (GTK_TABLE (effects_volnorm_table), volnorm_target_spin, 1, 2, 3, 4, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, volnorm_target_spin, "Set the maximum length for gaps at the end of a stream.\nDefault: 500", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (volnorm_target_spin), TRUE); + + volnorm_target_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (volnorm_target_hbox); + gtk_table_attach (GTK_TABLE (effects_volnorm_table), volnorm_target_hbox, 0, 1, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + volnorm_target_label = gtk_label_new ("Target RMS:"); + gtk_widget_show (volnorm_target_label); + gtk_box_pack_start (GTK_BOX (volnorm_target_hbox), volnorm_target_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (volnorm_target_label), GTK_JUSTIFY_CENTER); + + volnorm_enable_check = gtk_check_button_new_with_mnemonic ("Enable"); + gtk_widget_show (volnorm_enable_check); + gtk_table_attach (GTK_TABLE (effects_volnorm_table), volnorm_enable_check, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + volnorm_rva2_check = gtk_check_button_new_with_mnemonic ("Read ID3V2 RVA2 Tag"); + gtk_widget_show (volnorm_rva2_check); + gtk_table_attach (GTK_TABLE (effects_volnorm_table), volnorm_rva2_check, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label11 = gtk_label_new ("Volume normalizer"); + gtk_widget_show (label11); + gtk_frame_set_label_widget (GTK_FRAME (effects_volnorm_frame), label11); + + effects_help_label = gtk_label_new ("You do not need to press <b>Apply</b> after making changes to the effect plugin settings. It may take a while though until you can hear the change, since the plugin is applied before the mixing buffer."); + gtk_widget_show (effects_help_label); + gtk_box_pack_start (GTK_BOX (config_effects_page), effects_help_label, TRUE, TRUE, 0); + gtk_label_set_use_markup (GTK_LABEL (effects_help_label), TRUE); + gtk_label_set_line_wrap (GTK_LABEL (effects_help_label), TRUE); + + config_effects_label = gtk_label_new ("Effects"); + gtk_widget_show (config_effects_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (config_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (config_notebook), 1), config_effects_label); + gtk_label_set_justify (GTK_LABEL (config_effects_label), GTK_JUSTIFY_CENTER); + + config_crossfader_page = gtk_vbox_new (FALSE, 2); + gtk_widget_show (config_crossfader_page); + gtk_container_add (GTK_CONTAINER (config_notebook), config_crossfader_page); + gtk_container_set_border_width (GTK_CONTAINER (config_crossfader_page), 5); + + xf_bufsize_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (xf_bufsize_hbox); + gtk_box_pack_start (GTK_BOX (config_crossfader_page), xf_bufsize_hbox, FALSE, FALSE, 0); + + xf_bufsize_label = gtk_label_new ("Mixing buffer size (ms):"); + gtk_widget_show (xf_bufsize_label); + gtk_box_pack_start (GTK_BOX (xf_bufsize_hbox), xf_bufsize_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (xf_bufsize_label), GTK_JUSTIFY_CENTER); + + xf_buffer_spin_adj = gtk_adjustment_new (8500, 0, 60000, 100, 1000, 10); + xf_buffer_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xf_buffer_spin_adj), 0, 0); + gtk_widget_show (xf_buffer_spin); + gtk_box_pack_start (GTK_BOX (xf_bufsize_hbox), xf_buffer_spin, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, xf_buffer_spin, "Specify the size of the mixing buffer. Mixing buffer space is required for pauses, fade-outs, offsetting (i.e. interleaving the end and beginning of two songs) and killing trailing gaps. Fade-ins and killing leading gaps are done on-the-fly and do not depend on mixing buffer space.\nDefault: n/a - calculated automatically", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xf_buffer_spin), TRUE); + + xf_autobuf_check = gtk_check_button_new_with_mnemonic ("auto"); + gtk_widget_show (xf_autobuf_check); + gtk_box_pack_start (GTK_BOX (xf_bufsize_hbox), xf_autobuf_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, xf_autobuf_check, "Automatically calculate an optimal buffer size.\nDefault: On", NULL); + + xf_config_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (xf_config_hbox); + gtk_box_pack_start (GTK_BOX (config_crossfader_page), xf_config_hbox, FALSE, FALSE, 0); + + xf_config_label = gtk_label_new ("Set parameters for:"); + gtk_widget_show (xf_config_label); + gtk_box_pack_start (GTK_BOX (xf_config_hbox), xf_config_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (xf_config_label), GTK_JUSTIFY_CENTER); + + xf_config_optionmenu = gtk_option_menu_new (); + gtk_widget_show (xf_config_optionmenu); + gtk_box_pack_start (GTK_BOX (xf_config_hbox), xf_config_optionmenu, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, xf_config_optionmenu, "Select the type of songchange you want to configure:\n* Start of playback: When starting playback by pressing PLAY\n* Automatic songchange: When reaching the end of a song and the playlist advances to the next song\n* Manual songchange: When manually selecting another song, for example by pressing NEXT/PREV\n* Manual stop: When pressing the STOP button\n* End of playlist: After the last song in the playlist has been played\n* Seeking: When seeking within the current song", NULL); + + xf_config_optionmenu_menu = gtk_menu_new (); + + xf_config_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (xf_config_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (xf_config_optionmenu_menu), xf_config_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (xf_config_optionmenu), xf_config_optionmenu_menu); + + xf_type_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (xf_type_hbox); + gtk_box_pack_start (GTK_BOX (config_crossfader_page), xf_type_hbox, FALSE, FALSE, 0); + + xf_type_label = gtk_label_new ("Crossfade/Transition type:"); + gtk_widget_show (xf_type_label); + gtk_box_pack_start (GTK_BOX (xf_type_hbox), xf_type_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (xf_type_label), GTK_JUSTIFY_CENTER); + + xf_type_optionmenu = gtk_option_menu_new (); + gtk_widget_show (xf_type_optionmenu); + gtk_box_pack_start (GTK_BOX (xf_type_hbox), xf_type_optionmenu, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, xf_type_optionmenu, "Select the transition to be used for the songchange type selected above:\n* Fade-in: Fade-in at 'Start of playback'\n* Reopen output device: Force a close/open on the output plugin. This will most certainly re-introduce gaps and clicks, but might be usefull with some plugins.\n* Flush output device: Keeps the output plugin opened, but flushes its buffers. This will give you almost instant reaction when pressing NEXT/PREV.\n* None (gapless/off): Gapless mode. Keeps the device opened, but does not do any fading.\n* Simple crossfade: Does a simple crossfade between the previous and the next song.\n* Advanced crossfade: Allows you to configure the crossfade in more detail.\n* Fade-out: Fade-out at 'Manual stop' or 'End of playlist'.", NULL); + + xf_type_optionmenu_menu = gtk_menu_new (); + + xf_type_optionmenu_dummy = gtk_menu_item_new_with_mnemonic ("dummy"); + gtk_widget_show (xf_type_optionmenu_dummy); + gtk_container_add (GTK_CONTAINER (xf_type_optionmenu_menu), xf_type_optionmenu_dummy); + + gtk_option_menu_set_menu (GTK_OPTION_MENU (xf_type_optionmenu), xf_type_optionmenu_menu); + + xf_type_notebook = gtk_notebook_new (); + gtk_widget_show (xf_type_notebook); + gtk_box_pack_start (GTK_BOX (config_crossfader_page), xf_type_notebook, FALSE, FALSE, 0); + GTK_WIDGET_UNSET_FLAGS (xf_type_notebook, GTK_CAN_FOCUS); + gtk_notebook_set_show_tabs (GTK_NOTEBOOK (xf_type_notebook), FALSE); + gtk_notebook_set_show_border (GTK_NOTEBOOK (xf_type_notebook), FALSE); + + empty_notebook_page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (empty_notebook_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), empty_notebook_page); + + xft_reopen_label = gtk_label_new ("Reopen"); + gtk_widget_show (xft_reopen_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 0), xft_reopen_label); + gtk_label_set_justify (GTK_LABEL (xft_reopen_label), GTK_JUSTIFY_CENTER); + + xft_flush_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (xft_flush_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_flush_page); + + xftf_pause_frame = gtk_frame_new (NULL); + gtk_widget_show (xftf_pause_frame); + gtk_box_pack_start (GTK_BOX (xft_flush_page), xftf_pause_frame, FALSE, FALSE, 0); + + xftf_pause_table = gtk_table_new (2, 2, FALSE); + gtk_widget_show (xftf_pause_table); + gtk_container_add (GTK_CONTAINER (xftf_pause_frame), xftf_pause_table); + gtk_container_set_border_width (GTK_CONTAINER (xftf_pause_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xftf_pause_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xftf_pause_table), 5); + + xftfp_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (xftfp_length_label); + gtk_table_attach (GTK_TABLE (xftf_pause_table), xftfp_length_label, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + xftfp_length_spin_adj = gtk_adjustment_new (1000, 0, 60000, 100, 1000, 10); + xftfp_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfp_length_spin_adj), 0, 0); + gtk_widget_show (xftfp_length_spin); + gtk_table_attach (GTK_TABLE (xftf_pause_table), xftfp_length_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfp_length_spin, "Specify the length of the silence to be inserted between the tracks.\nDefault: 2000", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfp_length_spin), TRUE); + + xftfp_enable_check = gtk_check_button_new_with_mnemonic ("Enable"); + gtk_widget_show (xftfp_enable_check); + gtk_table_attach (GTK_TABLE (xftf_pause_table), xftfp_enable_check, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label12 = gtk_label_new ("Pause"); + gtk_widget_show (label12); + gtk_frame_set_label_widget (GTK_FRAME (xftf_pause_frame), label12); + + xftf_fadein_frame = gtk_frame_new (NULL); + gtk_widget_show (xftf_fadein_frame); + gtk_box_pack_start (GTK_BOX (xft_flush_page), xftf_fadein_frame, FALSE, FALSE, 0); + + xftf_fadein_table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (xftf_fadein_table); + gtk_container_add (GTK_CONTAINER (xftf_fadein_frame), xftf_fadein_table); + gtk_container_set_border_width (GTK_CONTAINER (xftf_fadein_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xftf_fadein_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xftf_fadein_table), 5); + + xftffi_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftffi_length_hbox); + gtk_table_attach (GTK_TABLE (xftf_fadein_table), xftffi_length_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftffi_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (xftffi_length_label); + gtk_box_pack_start (GTK_BOX (xftffi_length_hbox), xftffi_length_label, FALSE, FALSE, 0); + + xftffi_volume_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftffi_volume_hbox); + gtk_table_attach (GTK_TABLE (xftf_fadein_table), xftffi_volume_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftffi_volume_label = gtk_label_new ("Start volume (%):"); + gtk_widget_show (xftffi_volume_label); + gtk_box_pack_start (GTK_BOX (xftffi_volume_hbox), xftffi_volume_label, FALSE, FALSE, 0); + + xftffi_length_spin_adj = gtk_adjustment_new (1000, 0, 60000, 100, 1000, 10); + xftffi_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftffi_length_spin_adj), 0, 0); + gtk_widget_show (xftffi_length_spin); + gtk_table_attach (GTK_TABLE (xftf_fadein_table), xftffi_length_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftffi_length_spin, "Set the duration for the fadein of the next song.", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftffi_length_spin), TRUE); + + xftffi_enable_check = gtk_check_button_new_with_mnemonic ("Enable"); + gtk_widget_show (xftffi_enable_check); + gtk_table_attach (GTK_TABLE (xftf_fadein_table), xftffi_enable_check, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + xftffi_volume_spin_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); + xftffi_volume_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftffi_volume_spin_adj), 0, 0); + gtk_widget_show (xftffi_volume_spin); + gtk_table_attach (GTK_TABLE (xftf_fadein_table), xftffi_volume_spin, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftffi_volume_spin, "Set the start volume. XMMS-crossfade will fade from this volume to 100% during the time specified above.\nDefault: 0", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftffi_volume_spin), TRUE); + + label13 = gtk_label_new ("Fade in"); + gtk_widget_show (label13); + gtk_frame_set_label_widget (GTK_FRAME (xftf_fadein_frame), label13); + + xft_flush_label = gtk_label_new ("Flush"); + gtk_widget_show (xft_flush_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 1), xft_flush_label); + gtk_label_set_justify (GTK_LABEL (xft_flush_label), GTK_JUSTIFY_CENTER); + + empty_notebook_page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (empty_notebook_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), empty_notebook_page); + + xft_none_label = gtk_label_new ("None"); + gtk_widget_show (xft_none_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 2), xft_none_label); + gtk_label_set_justify (GTK_LABEL (xft_none_label), GTK_JUSTIFY_CENTER); + + xft_pause_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (xft_pause_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_pause_page); + + xf_pause_frame = gtk_frame_new (NULL); + gtk_widget_show (xf_pause_frame); + gtk_box_pack_start (GTK_BOX (xft_pause_page), xf_pause_frame, FALSE, FALSE, 0); + + xf_pause_table = gtk_table_new (1, 2, FALSE); + gtk_widget_show (xf_pause_table); + gtk_container_add (GTK_CONTAINER (xf_pause_frame), xf_pause_table); + gtk_container_set_border_width (GTK_CONTAINER (xf_pause_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xf_pause_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xf_pause_table), 5); + + pause_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (pause_length_hbox); + gtk_table_attach (GTK_TABLE (xf_pause_table), pause_length_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + pause_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (pause_length_label); + gtk_box_pack_start (GTK_BOX (pause_length_hbox), pause_length_label, FALSE, FALSE, 0); + + pause_length_spin_adj = gtk_adjustment_new (1000, 0, 60000, 100, 1000, 10); + pause_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (pause_length_spin_adj), 0, 0); + gtk_widget_show (pause_length_spin); + gtk_table_attach (GTK_TABLE (xf_pause_table), pause_length_spin, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, pause_length_spin, "Specify the length of the silence to be inserted between the tracks.\nDefault: 2000", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (pause_length_spin), TRUE); + + label14 = gtk_label_new ("Pause"); + gtk_widget_show (label14); + gtk_frame_set_label_widget (GTK_FRAME (xf_pause_frame), label14); + + xft_pause_label = gtk_label_new ("Pause"); + gtk_widget_show (xft_pause_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 3), xft_pause_label); + gtk_label_set_justify (GTK_LABEL (xft_pause_label), GTK_JUSTIFY_CENTER); + + xft_simplexf_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (xft_simplexf_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_simplexf_page); + + xf_simple_frame = gtk_frame_new (NULL); + gtk_widget_show (xf_simple_frame); + gtk_box_pack_start (GTK_BOX (xft_simplexf_page), xf_simple_frame, FALSE, FALSE, 0); + + xf_simple_table = gtk_table_new (1, 2, FALSE); + gtk_widget_show (xf_simple_table); + gtk_container_add (GTK_CONTAINER (xf_simple_frame), xf_simple_table); + gtk_container_set_border_width (GTK_CONTAINER (xf_simple_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xf_simple_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xf_simple_table), 5); + + simple_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (simple_length_hbox); + gtk_table_attach (GTK_TABLE (xf_simple_table), simple_length_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + simple_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (simple_length_label); + gtk_box_pack_start (GTK_BOX (simple_length_hbox), simple_length_label, FALSE, FALSE, 0); + + simple_length_spin_adj = gtk_adjustment_new (1500, 0, 60000, 100, 1000, 10); + simple_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (simple_length_spin_adj), 0, 0); + gtk_widget_show (simple_length_spin); + gtk_table_attach (GTK_TABLE (xf_simple_table), simple_length_spin, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, simple_length_spin, "Specify the length of the crosssfade.\nDefault: depends on songchange type", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (simple_length_spin), TRUE); + + label15 = gtk_label_new ("Crossfade"); + gtk_widget_show (label15); + gtk_frame_set_label_widget (GTK_FRAME (xf_simple_frame), label15); + + xft_simplexf_label = gtk_label_new ("Simple XF"); + gtk_widget_show (xft_simplexf_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 4), xft_simplexf_label); + gtk_label_set_justify (GTK_LABEL (xft_simplexf_label), GTK_JUSTIFY_CENTER); + + xft_advancedxf_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (xft_advancedxf_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_advancedxf_page); + + xf_fadeout_frame = gtk_frame_new (NULL); + gtk_widget_show (xf_fadeout_frame); + gtk_box_pack_start (GTK_BOX (xft_advancedxf_page), xf_fadeout_frame, FALSE, FALSE, 0); + + xf_fadeout_table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (xf_fadeout_table); + gtk_container_add (GTK_CONTAINER (xf_fadeout_frame), xf_fadeout_table); + gtk_container_set_border_width (GTK_CONTAINER (xf_fadeout_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xf_fadeout_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xf_fadeout_table), 5); + + fadeout_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (fadeout_length_hbox); + gtk_table_attach (GTK_TABLE (xf_fadeout_table), fadeout_length_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + fadeout_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (fadeout_length_label); + gtk_box_pack_start (GTK_BOX (fadeout_length_hbox), fadeout_length_label, FALSE, FALSE, 0); + + fadeout_length_spin_adj = gtk_adjustment_new (1000, 0, 60000, 100, 1000, 10); + fadeout_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (fadeout_length_spin_adj), 0, 0); + gtk_widget_show (fadeout_length_spin); + gtk_table_attach (GTK_TABLE (xf_fadeout_table), fadeout_length_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, fadeout_length_spin, "Set the duration for the fadeout of the previous song.", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (fadeout_length_spin), TRUE); + + fadeout_volume_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (fadeout_volume_hbox); + gtk_table_attach (GTK_TABLE (xf_fadeout_table), fadeout_volume_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + fadeout_volume_label = gtk_label_new ("End volume (%):"); + gtk_widget_show (fadeout_volume_label); + gtk_box_pack_start (GTK_BOX (fadeout_volume_hbox), fadeout_volume_label, FALSE, FALSE, 0); + + fadeout_volume_spin_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); + fadeout_volume_spin = gtk_spin_button_new (GTK_ADJUSTMENT (fadeout_volume_spin_adj), 0, 0); + gtk_widget_show (fadeout_volume_spin); + gtk_table_attach (GTK_TABLE (xf_fadeout_table), fadeout_volume_spin, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, fadeout_volume_spin, "Set the end volume. XMMS-crossfade will fade from 100% to this volume during the time specified above.\nDefault: 0", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (fadeout_volume_spin), TRUE); + + fadeout_options_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (fadeout_options_hbox); + gtk_table_attach (GTK_TABLE (xf_fadeout_table), fadeout_options_hbox, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + fadeout_enable_check = gtk_check_button_new_with_mnemonic ("Enable"); + gtk_widget_show (fadeout_enable_check); + gtk_box_pack_start (GTK_BOX (fadeout_options_hbox), fadeout_enable_check, FALSE, FALSE, 0); + + label16 = gtk_label_new ("Fade out"); + gtk_widget_show (label16); + gtk_frame_set_label_widget (GTK_FRAME (xf_fadeout_frame), label16); + + xf_offset_frame = gtk_frame_new (NULL); + gtk_widget_show (xf_offset_frame); + gtk_box_pack_start (GTK_BOX (xft_advancedxf_page), xf_offset_frame, FALSE, FALSE, 0); + + xf_offset_table = gtk_table_new (4, 1, FALSE); + gtk_widget_show (xf_offset_table); + gtk_container_add (GTK_CONTAINER (xf_offset_frame), xf_offset_table); + gtk_container_set_border_width (GTK_CONTAINER (xf_offset_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xf_offset_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xf_offset_table), 5); + + xfofs_custom_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (xfofs_custom_hbox); + gtk_table_attach (GTK_TABLE (xf_offset_table), xfofs_custom_hbox, 0, 1, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xfofs_custom_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, "Custom (ms):"); + gtk_widget_show (xfofs_custom_radiobutton); + gtk_box_pack_start (GTK_BOX (xfofs_custom_hbox), xfofs_custom_radiobutton, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, xfofs_custom_radiobutton, "Set a custom offset.", NULL); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (xfofs_custom_radiobutton), xfofs_custom_radiobutton_group); + xfofs_custom_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (xfofs_custom_radiobutton)); + + xfofs_custom_spin_adj = gtk_adjustment_new (-2000, -60000, 60000, 100, 1000, 10); + xfofs_custom_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xfofs_custom_spin_adj), 0, 0); + gtk_widget_show (xfofs_custom_spin); + gtk_box_pack_start (GTK_BOX (xfofs_custom_hbox), xfofs_custom_spin, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, xfofs_custom_spin, "Set how much the end of the previous and the beginning of the next song should overlap.\n* Negative values indicate that the next song should start before the previous has reached the end.\n* A value of 0 means that the songs are concatenated seamlessly.\n* Positive values will yield insert silence inbetween..", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xfofs_custom_spin), TRUE); + + xfofs_none_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, "None (0 ms)"); + gtk_widget_show (xfofs_none_radiobutton); + gtk_table_attach (GTK_TABLE (xf_offset_table), xfofs_none_radiobutton, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xfofs_none_radiobutton, "Don't overlap the two songs. The next song will start immediatelly after the previous has ended.", NULL); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (xfofs_none_radiobutton), xfofs_custom_radiobutton_group); + xfofs_custom_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (xfofs_none_radiobutton)); + + xfofs_lockout_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, "Lock to fadeout length"); + gtk_widget_show (xfofs_lockout_radiobutton); + gtk_table_attach (GTK_TABLE (xf_offset_table), xfofs_lockout_radiobutton, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xfofs_lockout_radiobutton, "Overlap by the fadeout duration specified above.", NULL); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (xfofs_lockout_radiobutton), xfofs_custom_radiobutton_group); + xfofs_custom_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (xfofs_lockout_radiobutton)); + + xfofs_lockin_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, "Lock to fadein length"); + gtk_widget_show (xfofs_lockin_radiobutton); + gtk_table_attach (GTK_TABLE (xf_offset_table), xfofs_lockin_radiobutton, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xfofs_lockin_radiobutton, "Overlap by the fadein duration specified below..", NULL); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (xfofs_lockin_radiobutton), xfofs_custom_radiobutton_group); + xfofs_custom_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (xfofs_lockin_radiobutton)); + + label17 = gtk_label_new ("Offset"); + gtk_widget_show (label17); + gtk_frame_set_label_widget (GTK_FRAME (xf_offset_frame), label17); + + xf_fadein_frame = gtk_frame_new (NULL); + gtk_widget_show (xf_fadein_frame); + gtk_box_pack_start (GTK_BOX (xft_advancedxf_page), xf_fadein_frame, FALSE, FALSE, 0); + + xf_fadein_table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (xf_fadein_table); + gtk_container_add (GTK_CONTAINER (xf_fadein_frame), xf_fadein_table); + gtk_container_set_border_width (GTK_CONTAINER (xf_fadein_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xf_fadein_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xf_fadein_table), 5); + + fadein_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (fadein_length_hbox); + gtk_table_attach (GTK_TABLE (xf_fadein_table), fadein_length_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + fadein_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (fadein_length_label); + gtk_box_pack_start (GTK_BOX (fadein_length_hbox), fadein_length_label, FALSE, FALSE, 0); + + fadein_volume_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (fadein_volume_hbox); + gtk_table_attach (GTK_TABLE (xf_fadein_table), fadein_volume_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + fadein_volume_label = gtk_label_new ("Start volume (%):"); + gtk_widget_show (fadein_volume_label); + gtk_box_pack_start (GTK_BOX (fadein_volume_hbox), fadein_volume_label, FALSE, FALSE, 0); + + fadein_length_spin_adj = gtk_adjustment_new (1000, 0, 60000, 100, 1000, 10); + fadein_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (fadein_length_spin_adj), 0, 0); + gtk_widget_show (fadein_length_spin); + gtk_table_attach (GTK_TABLE (xf_fadein_table), fadein_length_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, fadein_length_spin, "Set the duration for the fadein of the next song.", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (fadein_length_spin), TRUE); + + fadein_enable_check = gtk_check_button_new_with_mnemonic (" Enable"); + gtk_widget_show (fadein_enable_check); + gtk_table_attach (GTK_TABLE (xf_fadein_table), fadein_enable_check, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + fadein_lock_check = gtk_check_button_new_with_mnemonic ("Lock to Fade-out"); + gtk_widget_show (fadein_lock_check); + gtk_table_attach (GTK_TABLE (xf_fadein_table), fadein_lock_check, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, fadein_lock_check, "Use the same settings as fade-out.", NULL); + + fadein_volume_spin_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); + fadein_volume_spin = gtk_spin_button_new (GTK_ADJUSTMENT (fadein_volume_spin_adj), 0, 0); + gtk_widget_show (fadein_volume_spin); + gtk_table_attach (GTK_TABLE (xf_fadein_table), fadein_volume_spin, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, fadein_volume_spin, "Set the start volume. XMMS-crossfade will fade from this volume to 100% during the time specified above.\nDefault: 0", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (fadein_volume_spin), TRUE); + + label18 = gtk_label_new ("Fade in"); + gtk_widget_show (label18); + gtk_frame_set_label_widget (GTK_FRAME (xf_fadein_frame), label18); + + xft_advancedxf_label = gtk_label_new ("Advanced XF"); + gtk_widget_show (xft_advancedxf_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 5), xft_advancedxf_label); + gtk_label_set_justify (GTK_LABEL (xft_advancedxf_label), GTK_JUSTIFY_CENTER); + + xft_fadein_page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (xft_fadein_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_fadein_page); + + xftfi_fadein_frame = gtk_frame_new (NULL); + gtk_widget_show (xftfi_fadein_frame); + gtk_box_pack_start (GTK_BOX (xft_fadein_page), xftfi_fadein_frame, FALSE, FALSE, 0); + + xftfi_fadein_table = gtk_table_new (2, 2, FALSE); + gtk_widget_show (xftfi_fadein_table); + gtk_container_add (GTK_CONTAINER (xftfi_fadein_frame), xftfi_fadein_table); + gtk_container_set_border_width (GTK_CONTAINER (xftfi_fadein_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xftfi_fadein_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xftfi_fadein_table), 5); + + xftfi_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfi_length_hbox); + gtk_table_attach (GTK_TABLE (xftfi_fadein_table), xftfi_length_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftfi_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (xftfi_length_label); + gtk_box_pack_start (GTK_BOX (xftfi_length_hbox), xftfi_length_label, FALSE, FALSE, 0); + + xftfi_volume_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfi_volume_hbox); + gtk_table_attach (GTK_TABLE (xftfi_fadein_table), xftfi_volume_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftfi_volume_label = gtk_label_new ("Start volume (%):"); + gtk_widget_show (xftfi_volume_label); + gtk_box_pack_start (GTK_BOX (xftfi_volume_hbox), xftfi_volume_label, FALSE, FALSE, 0); + + xftfi_length_spin_adj = gtk_adjustment_new (1000, 0, 60000, 10, 100, 10); + xftfi_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfi_length_spin_adj), 0, 0); + gtk_widget_show (xftfi_length_spin); + gtk_table_attach (GTK_TABLE (xftfi_fadein_table), xftfi_length_spin, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfi_length_spin, "Set the duration for the fadein when starting playback.", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfi_length_spin), TRUE); + + xftfi_volume_spin_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); + xftfi_volume_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfi_volume_spin_adj), 0, 0); + gtk_widget_show (xftfi_volume_spin); + gtk_table_attach (GTK_TABLE (xftfi_fadein_table), xftfi_volume_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfi_volume_spin, "Set the start volume. XMMS-crossfade will fade from this volume to 100% during the time specified above.\nDefault: 0", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfi_volume_spin), TRUE); + + label19 = gtk_label_new ("Fade in"); + gtk_widget_show (label19); + gtk_frame_set_label_widget (GTK_FRAME (xftfi_fadein_frame), label19); + + xft_fadein_label = gtk_label_new ("Fade in"); + gtk_widget_show (xft_fadein_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 6), xft_fadein_label); + gtk_label_set_justify (GTK_LABEL (xft_fadein_label), GTK_JUSTIFY_CENTER); + + xft_fadeout_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (xft_fadeout_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_fadeout_page); + + xftfo_fadeout_frame = gtk_frame_new (NULL); + gtk_widget_show (xftfo_fadeout_frame); + gtk_box_pack_start (GTK_BOX (xft_fadeout_page), xftfo_fadeout_frame, FALSE, FALSE, 0); + + xftfo_fadeout_table = gtk_table_new (2, 2, FALSE); + gtk_widget_show (xftfo_fadeout_table); + gtk_container_add (GTK_CONTAINER (xftfo_fadeout_frame), xftfo_fadeout_table); + gtk_container_set_border_width (GTK_CONTAINER (xftfo_fadeout_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xftfo_fadeout_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xftfo_fadeout_table), 5); + + xftfo_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfo_length_hbox); + gtk_table_attach (GTK_TABLE (xftfo_fadeout_table), xftfo_length_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftfo_length_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (xftfo_length_label); + gtk_box_pack_start (GTK_BOX (xftfo_length_hbox), xftfo_length_label, FALSE, FALSE, 0); + + xftfo_length_spin_adj = gtk_adjustment_new (500, 0, 60000, 10, 100, 10); + xftfo_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfo_length_spin_adj), 0, 0); + gtk_widget_show (xftfo_length_spin); + gtk_table_attach (GTK_TABLE (xftfo_fadeout_table), xftfo_length_spin, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfo_length_spin, "Set the duration for the fadeout of the last song.", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfo_length_spin), TRUE); + + xftfo_volume_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfo_volume_hbox); + gtk_table_attach (GTK_TABLE (xftfo_fadeout_table), xftfo_volume_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftfo_volume_label = gtk_label_new ("End volume (%):"); + gtk_widget_show (xftfo_volume_label); + gtk_box_pack_start (GTK_BOX (xftfo_volume_hbox), xftfo_volume_label, FALSE, FALSE, 0); + + xftfo_volume_spin_adj = gtk_adjustment_new (0, 0, 100, 1, 10, 10); + xftfo_volume_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfo_volume_spin_adj), 0, 0); + gtk_widget_show (xftfo_volume_spin); + gtk_table_attach (GTK_TABLE (xftfo_fadeout_table), xftfo_volume_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfo_volume_spin, "Set the end volume. XMMS-crossfade will fade from 100% to this volume during the time specified above.\nDefault: 0", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfo_volume_spin), TRUE); + + label20 = gtk_label_new ("Fade out"); + gtk_widget_show (label20); + gtk_frame_set_label_widget (GTK_FRAME (xftfo_fadeout_frame), label20); + + xftfo_silence_frame = gtk_frame_new (NULL); + gtk_widget_show (xftfo_silence_frame); + gtk_box_pack_start (GTK_BOX (xft_fadeout_page), xftfo_silence_frame, FALSE, FALSE, 0); + + xftfo_silence_table = gtk_table_new (1, 2, FALSE); + gtk_widget_show (xftfo_silence_table); + gtk_container_add (GTK_CONTAINER (xftfo_silence_frame), xftfo_silence_table); + gtk_container_set_border_width (GTK_CONTAINER (xftfo_silence_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xftfo_silence_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xftfo_silence_table), 5); + + xftfo_silence_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfo_silence_hbox); + gtk_table_attach (GTK_TABLE (xftfo_silence_table), xftfo_silence_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + xftfo_silence_label = gtk_label_new ("Length (ms):"); + gtk_widget_show (xftfo_silence_label); + gtk_box_pack_start (GTK_BOX (xftfo_silence_hbox), xftfo_silence_label, FALSE, FALSE, 0); + + xftfo_silence_spin_adj = gtk_adjustment_new (100, 0, 60000, 10, 100, 10); + xftfo_silence_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfo_silence_spin_adj), 0, 0); + gtk_widget_show (xftfo_silence_spin); + gtk_table_attach (GTK_TABLE (xftfo_silence_table), xftfo_silence_spin, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfo_silence_spin, "Set how much additional silence should be played after the end of the last song. This way, you can avoid the clicks some soundcards produce when being shut down during playback.\nDefault: 500", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfo_silence_spin), TRUE); + + label21 = gtk_label_new ("Additional silence"); + gtk_widget_show (label21); + gtk_frame_set_label_widget (GTK_FRAME (xftfo_silence_frame), label21); + + xft_fadeout_label = gtk_label_new ("Fade out"); + gtk_widget_show (xft_fadeout_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 7), xft_fadeout_label); + gtk_label_set_justify (GTK_LABEL (xft_fadeout_label), GTK_JUSTIFY_CENTER); + + empty_notebook_page = gtk_vbox_new (FALSE, 0); + gtk_widget_show (empty_notebook_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), empty_notebook_page); + + xft_pause_none_label = gtk_label_new ("None"); + gtk_widget_show (xft_pause_none_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 8), xft_pause_none_label); + gtk_label_set_justify (GTK_LABEL (xft_pause_none_label), GTK_JUSTIFY_CENTER); + + xft_pause_adv_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (xft_pause_adv_page); + gtk_container_add (GTK_CONTAINER (xf_type_notebook), xft_pause_adv_page); + + xft_fadeoutin_frame = gtk_frame_new (NULL); + gtk_widget_show (xft_fadeoutin_frame); + gtk_box_pack_start (GTK_BOX (xft_pause_adv_page), xft_fadeoutin_frame, FALSE, FALSE, 0); + + xft_fadeoutin_table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (xft_fadeoutin_table); + gtk_container_add (GTK_CONTAINER (xft_fadeoutin_frame), xft_fadeoutin_table); + gtk_container_set_border_width (GTK_CONTAINER (xft_fadeoutin_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (xft_fadeoutin_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (xft_fadeoutin_table), 5); + + xftfoi_fadeout_spin_adj = gtk_adjustment_new (100, 0, 60000, 10, 100, 10); + xftfoi_fadeout_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfoi_fadeout_spin_adj), 0, 0); + gtk_widget_show (xftfoi_fadeout_spin); + gtk_table_attach (GTK_TABLE (xft_fadeoutin_table), xftfoi_fadeout_spin, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfoi_fadeout_spin, "Set the duration for the fadeout when pausing.\nDefault: 100", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfoi_fadeout_spin), TRUE); + + xftfoi_silence_spin_adj = gtk_adjustment_new (100, 0, 60000, 10, 100, 10); + xftfoi_silence_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfoi_silence_spin_adj), 0, 0); + gtk_widget_show (xftfoi_silence_spin); + gtk_table_attach (GTK_TABLE (xft_fadeoutin_table), xftfoi_silence_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfoi_silence_spin, "Set how much additional silence should be played after pausing. This way, you can avoid the clicks some soundcards produce when entering pause.\nDefault: 100", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfoi_silence_spin), TRUE); + + xftfoi_fadein_spin_adj = gtk_adjustment_new (100, 0, 60000, 10, 100, 10); + xftfoi_fadein_spin = gtk_spin_button_new (GTK_ADJUSTMENT (xftfoi_fadein_spin_adj), 0, 0); + gtk_widget_show (xftfoi_fadein_spin); + gtk_table_attach (GTK_TABLE (xft_fadeoutin_table), xftfoi_fadein_spin, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, xftfoi_fadein_spin, "Set the duration for the fadein when unpausing.\nDefault: 100", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (xftfoi_fadein_spin), TRUE); + + xftfoi_fadein_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfoi_fadein_hbox); + gtk_table_attach (GTK_TABLE (xft_fadeoutin_table), xftfoi_fadein_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + xftfoi_fadein_label = gtk_label_new ("Fade out (ms):"); + gtk_widget_show (xftfoi_fadein_label); + gtk_box_pack_start (GTK_BOX (xftfoi_fadein_hbox), xftfoi_fadein_label, FALSE, FALSE, 0); + + xftfoi_silence_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfoi_silence_hbox); + gtk_table_attach (GTK_TABLE (xft_fadeoutin_table), xftfoi_silence_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + xftfoi_silence_label = gtk_label_new ("Silence (ms):"); + gtk_widget_show (xftfoi_silence_label); + gtk_box_pack_start (GTK_BOX (xftfoi_silence_hbox), xftfoi_silence_label, FALSE, FALSE, 0); + + xftfoi_fadeout_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (xftfoi_fadeout_hbox); + gtk_table_attach (GTK_TABLE (xft_fadeoutin_table), xftfoi_fadeout_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + xftfoi_fadeout_label = gtk_label_new ("Fade in (ms):"); + gtk_widget_show (xftfoi_fadeout_label); + gtk_box_pack_start (GTK_BOX (xftfoi_fadeout_hbox), xftfoi_fadeout_label, FALSE, FALSE, 0); + + label22 = gtk_label_new ("Fade out / Fade in"); + gtk_widget_show (label22); + gtk_frame_set_label_widget (GTK_FRAME (xft_fadeoutin_frame), label22); + + xft_pause_adv_label = gtk_label_new ("Fade out / Fade in"); + gtk_widget_show (xft_pause_adv_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (xf_type_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (xf_type_notebook), 9), xft_pause_adv_label); + gtk_label_set_justify (GTK_LABEL (xft_pause_adv_label), GTK_JUSTIFY_CENTER); + + config_crossfade_label = gtk_label_new ("Crossfader"); + gtk_widget_show (config_crossfade_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (config_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (config_notebook), 2), config_crossfade_label); + gtk_label_set_justify (GTK_LABEL (config_crossfade_label), GTK_JUSTIFY_CENTER); + + config_gapkiller_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (config_gapkiller_page); + gtk_container_add (GTK_CONTAINER (config_notebook), config_gapkiller_page); + gtk_container_set_border_width (GTK_CONTAINER (config_gapkiller_page), 5); + + gap_leading_frame = gtk_frame_new (NULL); + gtk_widget_show (gap_leading_frame); + gtk_box_pack_start (GTK_BOX (config_gapkiller_page), gap_leading_frame, FALSE, TRUE, 0); + + gap_leading_table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (gap_leading_table); + gtk_container_add (GTK_CONTAINER (gap_leading_frame), gap_leading_table); + gtk_container_set_border_width (GTK_CONTAINER (gap_leading_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (gap_leading_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (gap_leading_table), 5); + + lgap_length_spin_adj = gtk_adjustment_new (500, 0, 60000, 10, 100, 10); + lgap_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (lgap_length_spin_adj), 0, 0); + gtk_widget_show (lgap_length_spin); + gtk_table_attach (GTK_TABLE (gap_leading_table), lgap_length_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, lgap_length_spin, "Set the maximum length for gaps at the beginning of a stream.\nDefault: 500", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (lgap_length_spin), TRUE); + + lgap_level_spin_adj = gtk_adjustment_new (512, 0, 32767, 16, 256, 10); + lgap_level_spin = gtk_spin_button_new (GTK_ADJUSTMENT (lgap_level_spin_adj), 0, 0); + gtk_widget_show (lgap_level_spin); + gtk_table_attach (GTK_TABLE (gap_leading_table), lgap_level_spin, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, lgap_level_spin, "Set the maximum volume for gaps at the beginning of a stream. All samples below this value are considered as silent.\nDefault: 512", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (lgap_level_spin), TRUE); + + lgap_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (lgap_length_hbox); + gtk_table_attach (GTK_TABLE (gap_leading_table), lgap_length_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + lgap_length_label = gtk_label_new ("Max. length (ms):"); + gtk_widget_show (lgap_length_label); + gtk_box_pack_start (GTK_BOX (lgap_length_hbox), lgap_length_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (lgap_length_label), GTK_JUSTIFY_CENTER); + + lgap_level_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (lgap_level_hbox); + gtk_table_attach (GTK_TABLE (gap_leading_table), lgap_level_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + lgap_level_label = gtk_label_new ("Max. level (16bit sample):"); + gtk_widget_show (lgap_level_label); + gtk_box_pack_start (GTK_BOX (lgap_level_hbox), lgap_level_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (lgap_level_label), GTK_JUSTIFY_CENTER); + + lgap_enable_check = gtk_check_button_new_with_mnemonic ("Enable"); + gtk_widget_show (lgap_enable_check); + gtk_table_attach (GTK_TABLE (gap_leading_table), lgap_enable_check, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label23 = gtk_label_new ("Leading gap killer"); + gtk_widget_show (label23); + gtk_frame_set_label_widget (GTK_FRAME (gap_leading_frame), label23); + + gap_trailing_frame = gtk_frame_new (NULL); + gtk_widget_show (gap_trailing_frame); + gtk_box_pack_start (GTK_BOX (config_gapkiller_page), gap_trailing_frame, FALSE, TRUE, 0); + + gap_trailing_table = gtk_table_new (3, 2, FALSE); + gtk_widget_show (gap_trailing_table); + gtk_container_add (GTK_CONTAINER (gap_trailing_frame), gap_trailing_table); + gtk_container_set_border_width (GTK_CONTAINER (gap_trailing_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (gap_trailing_table), 2); + gtk_table_set_col_spacings (GTK_TABLE (gap_trailing_table), 5); + + tgap_length_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (tgap_length_hbox); + gtk_table_attach (GTK_TABLE (gap_trailing_table), tgap_length_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + tgap_length_label = gtk_label_new ("Max. length (ms):"); + gtk_widget_show (tgap_length_label); + gtk_box_pack_start (GTK_BOX (tgap_length_hbox), tgap_length_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (tgap_length_label), GTK_JUSTIFY_CENTER); + + tgap_level_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (tgap_level_hbox); + gtk_table_attach (GTK_TABLE (gap_trailing_table), tgap_level_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + tgap_level_label = gtk_label_new ("Max. level (16bit sample):"); + gtk_widget_show (tgap_level_label); + gtk_box_pack_start (GTK_BOX (tgap_level_hbox), tgap_level_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (tgap_level_label), GTK_JUSTIFY_CENTER); + + tgap_length_spin_adj = gtk_adjustment_new (500, 0, 60000, 10, 100, 10); + tgap_length_spin = gtk_spin_button_new (GTK_ADJUSTMENT (tgap_length_spin_adj), 0, 0); + gtk_widget_show (tgap_length_spin); + gtk_table_attach (GTK_TABLE (gap_trailing_table), tgap_length_spin, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, tgap_length_spin, "Set the maximum length for gaps at the end of a stream.\nDefault: 500", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (tgap_length_spin), TRUE); + + tgap_level_spin_adj = gtk_adjustment_new (512, 0, 32767, 1, 16, 10); + tgap_level_spin = gtk_spin_button_new (GTK_ADJUSTMENT (tgap_level_spin_adj), 0, 0); + gtk_widget_show (tgap_level_spin); + gtk_table_attach (GTK_TABLE (gap_trailing_table), tgap_level_spin, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, tgap_level_spin, "Set the maximum volume for gaps at the end of a stream. All samples below this value are considered as silent.\nDefault: 512", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (tgap_level_spin), TRUE); + + tgap_lock_check = gtk_check_button_new_with_mnemonic ("Lock to Leading"); + gtk_widget_show (tgap_lock_check); + gtk_table_attach (GTK_TABLE (gap_trailing_table), tgap_lock_check, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_tooltips_set_tip (tooltips, tgap_lock_check, "Use the same settings as above.", NULL); + + tgap_enable_check = gtk_check_button_new_with_mnemonic ("Enable"); + gtk_widget_show (tgap_enable_check); + gtk_table_attach (GTK_TABLE (gap_trailing_table), tgap_enable_check, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + label24 = gtk_label_new ("Trailing gap killer"); + gtk_widget_show (label24); + gtk_frame_set_label_widget (GTK_FRAME (gap_trailing_frame), label24); + + gap_advanced_frame = gtk_frame_new (NULL); + gtk_widget_show (gap_advanced_frame); + gtk_box_pack_start (GTK_BOX (config_gapkiller_page), gap_advanced_frame, FALSE, FALSE, 0); + + gap_advanced_vbox = gtk_vbox_new (FALSE, 0); + gtk_widget_show (gap_advanced_vbox); + gtk_container_add (GTK_CONTAINER (gap_advanced_frame), gap_advanced_vbox); + gtk_container_set_border_width (GTK_CONTAINER (gap_advanced_vbox), 5); + + gadv_crossing_check = gtk_check_button_new_with_mnemonic ("Skip to next zero crossing"); + gtk_widget_show (gadv_crossing_check); + gtk_box_pack_start (GTK_BOX (gap_advanced_vbox), gadv_crossing_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, gadv_crossing_check, "If enabled, XMMS-crossfade will take additional care to avoid clicks with pre-faded (gapless) tracks. XMMS-crossfade will cut off the end of the previous song (the beginning of the next song) only at a zero-crossing of the sample values.\nDefault: On", NULL); + + label25 = gtk_label_new ("Advanced"); + gtk_widget_show (label25); + gtk_frame_set_label_widget (GTK_FRAME (gap_advanced_frame), label25); + + config_gapkiller_label = gtk_label_new ("Gap Killer"); + gtk_widget_show (config_gapkiller_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (config_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (config_notebook), 3), config_gapkiller_label); + gtk_label_set_justify (GTK_LABEL (config_gapkiller_label), GTK_JUSTIFY_CENTER); + + config_misc_page = gtk_vbox_new (FALSE, 5); + gtk_widget_show (config_misc_page); + gtk_container_add (GTK_CONTAINER (config_notebook), config_misc_page); + gtk_container_set_border_width (GTK_CONTAINER (config_misc_page), 5); + + misc_debug_frame = gtk_frame_new (NULL); + gtk_widget_show (misc_debug_frame); + gtk_box_pack_start (GTK_BOX (config_misc_page), misc_debug_frame, FALSE, FALSE, 0); + + misc_debug_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (misc_debug_vbox); + gtk_container_add (GTK_CONTAINER (misc_debug_frame), misc_debug_vbox); + gtk_container_set_border_width (GTK_CONTAINER (misc_debug_vbox), 5); + + debug_stderr_check = gtk_check_button_new_with_mnemonic ("Write debug output to <stderr>"); + gtk_widget_show (debug_stderr_check); + gtk_box_pack_start (GTK_BOX (misc_debug_vbox), debug_stderr_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, debug_stderr_check, "Monitors what is going on in XMMS-crossfade. If you think you found a bug in XMMS-crossfade, please enable this option and send me the output.\nNote that you should disable debug output if you start XMMS from within Netscape. Netscape will spam you with dialogs containing the debug output captured from <stderr>.\nDefault: Off", NULL); + + debug_monitor_check = gtk_check_button_new_with_mnemonic ("Show Buffer Monitor"); + gtk_widget_show (debug_monitor_check); + gtk_box_pack_start (GTK_BOX (misc_debug_vbox), debug_monitor_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, debug_monitor_check, "Enables the Buffer Monitor. This is a small window which shows how much data is in the buffers. The top display belongs to the mixing buffer, the bottom shows how much data is being buffered by the output plugin.\nDefault: Off", NULL); + + label26 = gtk_label_new ("Debug options"); + gtk_widget_show (label26); + gtk_frame_set_label_widget (GTK_FRAME (misc_debug_frame), label26); + + misc_mixopt_frame = gtk_frame_new (NULL); + gtk_widget_show (misc_mixopt_frame); + gtk_box_pack_start (GTK_BOX (config_misc_page), misc_mixopt_frame, FALSE, FALSE, 0); + + misc_mixopt_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (misc_mixopt_vbox); + gtk_container_add (GTK_CONTAINER (misc_mixopt_frame), misc_mixopt_vbox); + gtk_container_set_border_width (GTK_CONTAINER (misc_mixopt_vbox), 5); + + mixopt_enable_check = gtk_check_button_new_with_mnemonic ("Enable volume control"); + gtk_widget_show (mixopt_enable_check); + gtk_box_pack_start (GTK_BOX (misc_mixopt_vbox), mixopt_enable_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, mixopt_enable_check, "Enables/disables XMMS volume and balance controls.\nDefault: On", NULL); + + mixopt_reverse_check = gtk_check_button_new_with_mnemonic ("Reverse balance"); + gtk_widget_show (mixopt_reverse_check); + gtk_box_pack_start (GTK_BOX (misc_mixopt_vbox), mixopt_reverse_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, mixopt_reverse_check, "Reverses left and right with the balance control.\nDefault: Off", NULL); + + mixopt_software_check = gtk_check_button_new_with_mnemonic ("Software Mixer"); + gtk_widget_show (mixopt_software_check); + gtk_box_pack_start (GTK_BOX (misc_mixopt_vbox), mixopt_software_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, mixopt_software_check, "Enable software mixer. Usefull for output plugins without mixer support.\nDefault: Off", NULL); + + label27 = gtk_label_new ("Volume control options"); + gtk_widget_show (label27); + gtk_frame_set_label_widget (GTK_FRAME (misc_mixopt_frame), label27); + + misc_other_frame = gtk_frame_new (NULL); + gtk_widget_show (misc_other_frame); + gtk_box_pack_start (GTK_BOX (config_misc_page), misc_other_frame, FALSE, TRUE, 0); + + misc_other_vbox = gtk_vbox_new (FALSE, 2); + gtk_widget_show (misc_other_vbox); + gtk_container_add (GTK_CONTAINER (misc_other_frame), misc_other_vbox); + gtk_container_set_border_width (GTK_CONTAINER (misc_other_vbox), 5); + + moth_songchange_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (moth_songchange_hbox); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_songchange_hbox, TRUE, TRUE, 0); + + moth_songchange_label = gtk_label_new ("Songchange timeout (ms):"); + gtk_widget_show (moth_songchange_label); + gtk_box_pack_start (GTK_BOX (moth_songchange_hbox), moth_songchange_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (moth_songchange_label), GTK_JUSTIFY_CENTER); + + moth_songchange_spin_adj = gtk_adjustment_new (100, 0, 5000, 10, 100, 10); + moth_songchange_spin = gtk_spin_button_new (GTK_ADJUSTMENT (moth_songchange_spin_adj), 0, 0); + gtk_widget_show (moth_songchange_spin); + gtk_box_pack_start (GTK_BOX (moth_songchange_hbox), moth_songchange_spin, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, moth_songchange_spin, "The songchange timeout is the maximum time XMMS-crossfade waits for the next song. If this timeout is exceeded, the output plugin/device is closed.\nDefault: 100", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (moth_songchange_spin), TRUE); + + moth_preload_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (moth_preload_hbox); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_preload_hbox, TRUE, TRUE, 0); + + moth_preload_label = gtk_label_new ("Mixing buffer preload (ms):"); + gtk_widget_show (moth_preload_label); + gtk_box_pack_start (GTK_BOX (moth_preload_hbox), moth_preload_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (moth_preload_label), GTK_JUSTIFY_CENTER); + + moth_preload_spin_adj = gtk_adjustment_new (0, 0, 60000, 10, 100, 10); + moth_preload_spin = gtk_spin_button_new (GTK_ADJUSTMENT (moth_preload_spin_adj), 0, 0); + gtk_widget_show (moth_preload_spin); + gtk_box_pack_start (GTK_BOX (moth_preload_hbox), moth_preload_spin, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, moth_preload_spin, "Tells XMMS-crossfade how much data it should buffer before it starts writing to the output plugin/device. Usually, this value can be set to 0, because the output plugin does preloading on its own.\nDefault: 0", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (moth_preload_spin), TRUE); + + moth_noxf_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (moth_noxf_hbox); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_noxf_hbox, FALSE, FALSE, 0); + + moth_noxf_label = gtk_label_new ("Don't crossfade"); + gtk_widget_show (moth_noxf_label); + gtk_box_pack_start (GTK_BOX (moth_noxf_hbox), moth_noxf_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (moth_noxf_label), GTK_JUSTIFY_CENTER); + + noxf_album_check = gtk_check_button_new_with_mnemonic ("successive tracks"); + gtk_widget_show (noxf_album_check); + gtk_box_pack_start (GTK_BOX (moth_noxf_hbox), noxf_album_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, noxf_album_check, "This option enables the automatic detection of pre-faded or gapless tracks, like the tracks on some compilations or on most live albums. If such tracks are detected, crossfading will be disabled and only the gapkiller (if enabled) will be used for the songchange.\nDefault: On", NULL); + + noxf_samefile_check = gtk_check_button_new_with_mnemonic ("same file"); + gtk_widget_show (noxf_samefile_check); + gtk_box_pack_start (GTK_BOX (moth_noxf_hbox), noxf_samefile_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, noxf_samefile_check, "This option disables crossfading between the same file. This can happen if you have only one file in the playlist and 'repeat' turned on.\nDefault: Off", NULL); + + moth_httpworkaround_check = gtk_check_button_new_with_mnemonic ("Enable HTTP buffer underrun workaround"); + gtk_widget_show (moth_httpworkaround_check); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_httpworkaround_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, moth_httpworkaround_check, "Enable this when you have problems playing internet HTTP audio streams. Unfortunatelly, it also breaks crossfading.\nDefault: Off", NULL); + + moth_opmaxused_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (moth_opmaxused_hbox); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_opmaxused_hbox, TRUE, TRUE, 0); + + moth_opmaxused_check = gtk_check_button_new_with_mnemonic ("Limit OP buffer usage (ms):"); + gtk_widget_show (moth_opmaxused_check); + gtk_box_pack_start (GTK_BOX (moth_opmaxused_hbox), moth_opmaxused_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, moth_opmaxused_check, "With this option enabled, XMMS-crossfade will limit the amount of data being buffered by the output plugin. This way, you can decrease the latency between pressing STOP/NEXT/PREV and the new song actually being played. \nDefault: Off", NULL); + + moth_opmaxused_spin_adj = gtk_adjustment_new (500, 10, 10000, 10, 100, 10); + moth_opmaxused_spin = gtk_spin_button_new (GTK_ADJUSTMENT (moth_opmaxused_spin_adj), 0, 0); + gtk_widget_show (moth_opmaxused_spin); + gtk_box_pack_start (GTK_BOX (moth_opmaxused_hbox), moth_opmaxused_spin, TRUE, TRUE, 0); + gtk_tooltips_set_tip (tooltips, moth_opmaxused_spin, "Beware that setting this value too low also increases the chance of buffer underruns.\nDefault: 250", NULL); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (moth_opmaxused_spin), TRUE); + + moth_quantaudio_check = gtk_check_button_new_with_mnemonic ("Use Quantaudio timing comments"); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_quantaudio_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, moth_quantaudio_check, "Enable this when you have problems playing internet HTTP audio streams. Unfortunatelly, it also breaks crossfading.\nDefault: Off", NULL); + + moth_outputkeepopened_check = gtk_check_button_new_with_mnemonic ("Keep output opened"); + gtk_widget_show (moth_outputkeepopened_check); + gtk_box_pack_start (GTK_BOX (misc_other_vbox), moth_outputkeepopened_check, FALSE, FALSE, 0); + gtk_tooltips_set_tip (tooltips, moth_outputkeepopened_check, "Keep the output device opened all the time, even if XMMS is stopped. This way, you can avoid clicks caused by the soundcard when the device is opened or closed\nDefault: Off", NULL); + + label28 = gtk_label_new ("Advanced options"); + gtk_widget_show (label28); + gtk_frame_set_label_widget (GTK_FRAME (misc_other_frame), label28); + + config_misc_label = gtk_label_new ("Advanced"); + gtk_widget_show (config_misc_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (config_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (config_notebook), 4), config_misc_label); + gtk_label_set_justify (GTK_LABEL (config_misc_label), GTK_JUSTIFY_CENTER); + + config_presets_page = gtk_vbox_new (FALSE, 5); + gtk_container_add (GTK_CONTAINER (config_notebook), config_presets_page); + gtk_container_set_border_width (GTK_CONTAINER (config_presets_page), 5); + + presets_name_hbox = gtk_hbox_new (FALSE, 5); + gtk_widget_show (presets_name_hbox); + gtk_box_pack_start (GTK_BOX (config_presets_page), presets_name_hbox, FALSE, FALSE, 0); + + presets_name_entry = gtk_entry_new (); + gtk_widget_show (presets_name_entry); + gtk_box_pack_start (GTK_BOX (presets_name_hbox), presets_name_entry, TRUE, TRUE, 0); + + presets_delete_button = gtk_button_new_with_mnemonic ("Delete"); + gtk_widget_show (presets_delete_button); + gtk_box_pack_start (GTK_BOX (presets_name_hbox), presets_delete_button, FALSE, FALSE, 0); + + presets_new_button = gtk_button_new_with_mnemonic ("New"); + gtk_widget_show (presets_new_button); + gtk_box_pack_start (GTK_BOX (presets_name_hbox), presets_new_button, FALSE, FALSE, 0); + + presets_list_scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (presets_list_scrolledwindow); + gtk_box_pack_start (GTK_BOX (config_presets_page), presets_list_scrolledwindow, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (presets_list_scrolledwindow, GTK_CAN_FOCUS); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (presets_list_scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (presets_list_scrolledwindow), GTK_SHADOW_IN); + + presets_list_list = gtk_tree_view_new (); + gtk_widget_show (presets_list_list); + gtk_container_add (GTK_CONTAINER (presets_list_scrolledwindow), presets_list_list); + + presets_list_bbox = gtk_hbutton_box_new (); + gtk_widget_show (presets_list_bbox); + gtk_box_pack_start (GTK_BOX (config_presets_page), presets_list_bbox, FALSE, FALSE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (presets_list_bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (presets_list_bbox), 5); + + presets_load_button = gtk_button_new_with_mnemonic ("Load"); + gtk_widget_show (presets_load_button); + gtk_container_add (GTK_CONTAINER (presets_list_bbox), presets_load_button); + GTK_WIDGET_SET_FLAGS (presets_load_button, GTK_CAN_DEFAULT); + + presets_save_button = gtk_button_new_with_mnemonic ("Save"); + gtk_widget_show (presets_save_button); + gtk_container_add (GTK_CONTAINER (presets_list_bbox), presets_save_button); + GTK_WIDGET_SET_FLAGS (presets_save_button, GTK_CAN_DEFAULT); + + config_presets_label = gtk_label_new ("Presets"); + gtk_widget_show (config_presets_label); + gtk_notebook_set_tab_label (GTK_NOTEBOOK (config_notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (config_notebook), 5), config_presets_label); + gtk_label_set_justify (GTK_LABEL (config_presets_label), GTK_JUSTIFY_CENTER); + + config_bbox = gtk_hbutton_box_new (); + gtk_widget_show (config_bbox); + gtk_box_pack_start (GTK_BOX (config_vbox), config_bbox, FALSE, TRUE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (config_bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (config_bbox), 5); + + config_ok = gtk_button_new_with_mnemonic ("OK"); + gtk_widget_show (config_ok); + gtk_container_add (GTK_CONTAINER (config_bbox), config_ok); + GTK_WIDGET_SET_FLAGS (config_ok, GTK_CAN_DEFAULT); + + config_cancel = gtk_button_new_with_mnemonic ("Cancel"); + gtk_widget_show (config_cancel); + gtk_container_add (GTK_CONTAINER (config_bbox), config_cancel); + GTK_WIDGET_SET_FLAGS (config_cancel, GTK_CAN_DEFAULT); + + config_apply = gtk_button_new_with_mnemonic ("Apply"); + gtk_widget_show (config_apply); + gtk_container_add (GTK_CONTAINER (config_bbox), config_apply); + GTK_WIDGET_SET_FLAGS (config_apply, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) output_oss_radio, "toggled", + G_CALLBACK (on_output_oss_radio_toggled), + NULL); + g_signal_connect ((gpointer) output_plugin_radio, "toggled", + G_CALLBACK (on_output_plugin_radio_toggled), + NULL); + g_signal_connect ((gpointer) output_none_radio, "toggled", + G_CALLBACK (on_output_none_radio_toggled), + NULL); + g_signal_connect ((gpointer) oss_adevice_alt_check, "toggled", + G_CALLBACK (on_config_adevice_alt_check_toggled), + NULL); + g_signal_connect ((gpointer) oss_mdevice_alt_check, "toggled", + G_CALLBACK (on_config_mdevice_alt_check_toggled), + NULL); + g_signal_connect ((gpointer) osshwb_maxbuf_check, "toggled", + G_CALLBACK (on_osshwb_maxbuf_check_toggled), + NULL); + g_signal_connect ((gpointer) op_configure_button, "clicked", + G_CALLBACK (on_output_plugin_configure_button_clicked), + NULL); + g_signal_connect ((gpointer) op_about_button, "clicked", + G_CALLBACK (on_output_plugin_about_button_clicked), + NULL); + g_signal_connect ((gpointer) op_throttle_check, "toggled", + G_CALLBACK (on_op_throttle_check_toggled), + NULL); + g_signal_connect ((gpointer) op_maxblock_check, "toggled", + G_CALLBACK (on_op_maxblock_check_toggled), + NULL); + g_signal_connect ((gpointer) op_maxblock_spin, "changed", + G_CALLBACK (on_op_maxblock_spin_changed), + NULL); + g_signal_connect ((gpointer) op_forcereopen_check, "toggled", + G_CALLBACK (on_op_forcereopen_check_toggled), + NULL); + g_signal_connect ((gpointer) ep_configure_button, "clicked", + G_CALLBACK (on_ep_configure_button_clicked), + NULL); + g_signal_connect ((gpointer) ep_about_button, "clicked", + G_CALLBACK (on_ep_about_button_clicked), + NULL); + g_signal_connect ((gpointer) ep_enable_check, "toggled", + G_CALLBACK (on_ep_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) volnorm_enable_check, "toggled", + G_CALLBACK (on_volnorm_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) xf_buffer_spin, "changed", + G_CALLBACK (on_xf_buffer_spin_changed), + NULL); + g_signal_connect ((gpointer) xf_autobuf_check, "toggled", + G_CALLBACK (on_xf_autobuf_check_toggled), + NULL); + g_signal_connect ((gpointer) xftfp_length_spin, "changed", + G_CALLBACK (on_xftfp_length_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfp_enable_check, "toggled", + G_CALLBACK (on_xftfp_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) xftffi_length_spin, "changed", + G_CALLBACK (on_xftffi_length_spin_changed), + NULL); + g_signal_connect ((gpointer) xftffi_enable_check, "toggled", + G_CALLBACK (on_xftffi_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) xftffi_volume_spin, "changed", + G_CALLBACK (on_xftffi_volume_spin_changed), + NULL); + g_signal_connect ((gpointer) pause_length_spin, "changed", + G_CALLBACK (on_pause_length_spin_changed), + NULL); + g_signal_connect ((gpointer) simple_length_spin, "changed", + G_CALLBACK (on_simple_length_spin_changed), + NULL); + g_signal_connect ((gpointer) fadeout_length_spin, "changed", + G_CALLBACK (on_fadeout_length_spin_changed), + NULL); + g_signal_connect ((gpointer) fadeout_volume_spin, "changed", + G_CALLBACK (on_fadeout_volume_spin_changed), + NULL); + g_signal_connect ((gpointer) fadeout_enable_check, "toggled", + G_CALLBACK (on_fadeout_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) xfofs_custom_radiobutton, "toggled", + G_CALLBACK (on_xfofs_custom_radiobutton_toggled), + NULL); + g_signal_connect ((gpointer) xfofs_custom_radiobutton, "clicked", + G_CALLBACK (on_xfofs_custom_radiobutton_clicked), + NULL); + g_signal_connect ((gpointer) xfofs_custom_spin, "changed", + G_CALLBACK (on_xfofs_custom_spin_changed), + NULL); + g_signal_connect ((gpointer) xfofs_none_radiobutton, "clicked", + G_CALLBACK (on_xfofs_none_radiobutton_clicked), + NULL); + g_signal_connect ((gpointer) xfofs_none_radiobutton, "toggled", + G_CALLBACK (on_xfofs_none_radiobutton_toggled), + NULL); + g_signal_connect ((gpointer) xfofs_lockout_radiobutton, "toggled", + G_CALLBACK (on_xfofs_lockout_radiobutton_toggled), + NULL); + g_signal_connect ((gpointer) xfofs_lockout_radiobutton, "clicked", + G_CALLBACK (on_xfofs_lockout_radiobutton_clicked), + NULL); + g_signal_connect ((gpointer) xfofs_lockin_radiobutton, "toggled", + G_CALLBACK (on_xfofs_lockin_radiobutton_toggled), + NULL); + g_signal_connect ((gpointer) xfofs_lockin_radiobutton, "clicked", + G_CALLBACK (on_xfofs_lockin_radiobutton_clicked), + NULL); + g_signal_connect ((gpointer) fadein_length_spin, "changed", + G_CALLBACK (on_fadein_length_spin_changed), + NULL); + g_signal_connect ((gpointer) fadein_enable_check, "toggled", + G_CALLBACK (on_fadein_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) fadein_lock_check, "toggled", + G_CALLBACK (on_fadein_lock_check_toggled), + NULL); + g_signal_connect ((gpointer) fadein_volume_spin, "changed", + G_CALLBACK (on_fadein_volume_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfi_length_spin, "changed", + G_CALLBACK (on_fadein_length_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfi_volume_spin, "changed", + G_CALLBACK (on_fadein_volume_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfo_length_spin, "changed", + G_CALLBACK (on_fadeout_length_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfo_volume_spin, "changed", + G_CALLBACK (on_fadeout_volume_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfo_silence_spin, "changed", + G_CALLBACK (on_xfofs_custom_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfoi_fadeout_spin, "changed", + G_CALLBACK (on_fadeout_length_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfoi_silence_spin, "changed", + G_CALLBACK (on_xfofs_custom_spin_changed), + NULL); + g_signal_connect ((gpointer) xftfoi_fadein_spin, "changed", + G_CALLBACK (on_fadein_length_spin_changed), + NULL); + g_signal_connect ((gpointer) lgap_length_spin, "changed", + G_CALLBACK (on_lgap_length_spin_changed), + NULL); + g_signal_connect ((gpointer) lgap_level_spin, "changed", + G_CALLBACK (on_lgap_level_spin_changed), + NULL); + g_signal_connect ((gpointer) lgap_enable_check, "toggled", + G_CALLBACK (on_lgap_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) tgap_length_spin, "changed", + G_CALLBACK (on_tgap_length_spin_changed), + NULL); + g_signal_connect ((gpointer) tgap_level_spin, "changed", + G_CALLBACK (on_tgap_level_spin_changed), + NULL); + g_signal_connect ((gpointer) tgap_lock_check, "toggled", + G_CALLBACK (on_tgap_lock_check_toggled), + NULL); + g_signal_connect ((gpointer) tgap_enable_check, "toggled", + G_CALLBACK (on_tgap_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) mixopt_enable_check, "toggled", + G_CALLBACK (on_config_mixopt_enable_check_toggled), + NULL); + g_signal_connect ((gpointer) moth_songchange_spin, "changed", + G_CALLBACK (on_moth_songchange_spin_changed), + NULL); + g_signal_connect ((gpointer) moth_opmaxused_check, "toggled", + G_CALLBACK (on_moth_opmaxused_check_toggled), + NULL); + g_signal_connect ((gpointer) config_ok, "clicked", + G_CALLBACK (on_config_ok_clicked), + NULL); + g_signal_connect_swapped ((gpointer) config_cancel, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (config_win)); + g_signal_connect ((gpointer) config_apply, "clicked", + G_CALLBACK (on_config_apply_clicked), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (config_win, config_win, "config_win"); + GLADE_HOOKUP_OBJECT (config_win, config_vbox, "config_vbox"); + GLADE_HOOKUP_OBJECT (config_win, config_notebook, "config_notebook"); + GLADE_HOOKUP_OBJECT (config_win, config_output_page, "config_output_page"); + GLADE_HOOKUP_OBJECT (config_win, output_options_hbox, "output_options_hbox"); + GLADE_HOOKUP_OBJECT (config_win, output_method_frame, "output_method_frame"); + GLADE_HOOKUP_OBJECT (config_win, output_method_vbox, "output_method_vbox"); + GLADE_HOOKUP_OBJECT (config_win, output_oss_radio, "output_oss_radio"); + GLADE_HOOKUP_OBJECT (config_win, output_plugin_radio, "output_plugin_radio"); + GLADE_HOOKUP_OBJECT (config_win, output_none_radio, "output_none_radio"); + GLADE_HOOKUP_OBJECT (config_win, output_method_frame_label, "output_method_frame_label"); + GLADE_HOOKUP_OBJECT (config_win, output_resampling_frame, "output_resampling_frame"); + GLADE_HOOKUP_OBJECT (config_win, output_resampling_table, "output_resampling_table"); + GLADE_HOOKUP_OBJECT (config_win, resampling_rate_optionmenu, "resampling_rate_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, menu1, "menu1"); + GLADE_HOOKUP_OBJECT (config_win, resampling_rate_optionmenu_dummy, "resampling_rate_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, resampling_quality_optionmenu, "resampling_quality_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, menu2, "menu2"); + GLADE_HOOKUP_OBJECT (config_win, resampling_quality_optionmenu_dummy, "resampling_quality_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, resampling_rate_hbox, "resampling_rate_hbox"); + GLADE_HOOKUP_OBJECT (config_win, resampling_rate_label, "resampling_rate_label"); + GLADE_HOOKUP_OBJECT (config_win, resampling_quality_hbox, "resampling_quality_hbox"); + GLADE_HOOKUP_OBJECT (config_win, resampling_quality_label, "resampling_quality_label"); + GLADE_HOOKUP_OBJECT (config_win, output_resampling_frame_label, "output_resampling_frame_label"); + GLADE_HOOKUP_OBJECT (config_win, output_notebook, "output_notebook"); + GLADE_HOOKUP_OBJECT (config_win, output_oss_page, "output_oss_page"); + GLADE_HOOKUP_OBJECT (config_win, output_oss_notebook, "output_oss_notebook"); + GLADE_HOOKUP_OBJECT (config_win, oss_device_page, "oss_device_page"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_frame, "oss_adevice_frame"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_vbox, "oss_adevice_vbox"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_hbox, "oss_adevice_hbox"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_optionmenu, "oss_adevice_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_optionmenu_menu, "oss_adevice_optionmenu_menu"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_optionmenu_dummy, "oss_adevice_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_alt_hbox, "oss_adevice_alt_hbox"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_alt_check, "oss_adevice_alt_check"); + GLADE_HOOKUP_OBJECT (config_win, oss_adevice_alt_entry, "oss_adevice_alt_entry"); + GLADE_HOOKUP_OBJECT (config_win, label3, "label3"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_frame, "oss_mdevice_frame"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_vbox, "oss_mdevice_vbox"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_hbox, "oss_mdevice_hbox"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_optionmenu, "oss_mdevice_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_optionmenu_menu, "oss_mdevice_optionmenu_menu"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_optionmenu_dummy, "oss_mdevice_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_alt_hbox, "oss_mdevice_alt_hbox"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_alt_check, "oss_mdevice_alt_check"); + GLADE_HOOKUP_OBJECT (config_win, oss_mdevice_alt_entry, "oss_mdevice_alt_entry"); + GLADE_HOOKUP_OBJECT (config_win, label4, "label4"); + GLADE_HOOKUP_OBJECT (config_win, oss_device_label, "oss_device_label"); + GLADE_HOOKUP_OBJECT (config_win, oss_buffer_page, "oss_buffer_page"); + GLADE_HOOKUP_OBJECT (config_win, oss_buffer_frame, "oss_buffer_frame"); + GLADE_HOOKUP_OBJECT (config_win, oss_buffer_vbox, "oss_buffer_vbox"); + GLADE_HOOKUP_OBJECT (config_win, ossbuf_buffer_hbox, "ossbuf_buffer_hbox"); + GLADE_HOOKUP_OBJECT (config_win, ossbuf_buffer_label, "ossbuf_buffer_label"); + GLADE_HOOKUP_OBJECT (config_win, ossbuf_buffer_spin, "ossbuf_buffer_spin"); + GLADE_HOOKUP_OBJECT (config_win, ossbuf_preload_hbox, "ossbuf_preload_hbox"); + GLADE_HOOKUP_OBJECT (config_win, ossbuf_preload_label, "ossbuf_preload_label"); + GLADE_HOOKUP_OBJECT (config_win, ossbuf_preload_spin, "ossbuf_preload_spin"); + GLADE_HOOKUP_OBJECT (config_win, label5, "label5"); + GLADE_HOOKUP_OBJECT (config_win, oss_hwbuf_frame, "oss_hwbuf_frame"); + GLADE_HOOKUP_OBJECT (config_win, oss_hwbuf_vbox, "oss_hwbuf_vbox"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_maxbuf_check, "osshwb_maxbuf_check"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_fragments_hbox, "osshwb_fragments_hbox"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_fragments_label, "osshwb_fragments_label"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_fragments_spin, "osshwb_fragments_spin"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_fragsize_hbox, "osshwb_fragsize_hbox"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_fragsize_label, "osshwb_fragsize_label"); + GLADE_HOOKUP_OBJECT (config_win, osshwb_fragsize_spin, "osshwb_fragsize_spin"); + GLADE_HOOKUP_OBJECT (config_win, label6, "label6"); + GLADE_HOOKUP_OBJECT (config_win, oss_buffer_label, "oss_buffer_label"); + GLADE_HOOKUP_OBJECT (config_win, oss_mixer_page, "oss_mixer_page"); + GLADE_HOOKUP_OBJECT (config_win, oss_mixer_frame, "oss_mixer_frame"); + GLADE_HOOKUP_OBJECT (config_win, oss_mixer_vbox, "oss_mixer_vbox"); + GLADE_HOOKUP_OBJECT (config_win, ossmixer_pcm_check, "ossmixer_pcm_check"); + GLADE_HOOKUP_OBJECT (config_win, label7, "label7"); + GLADE_HOOKUP_OBJECT (config_win, oss_mixer_label, "oss_mixer_label"); + GLADE_HOOKUP_OBJECT (config_win, output_plugin_label, "output_plugin_label"); + GLADE_HOOKUP_OBJECT (config_win, output_plugin_page, "output_plugin_page"); + GLADE_HOOKUP_OBJECT (config_win, op_plugin_frame, "op_plugin_frame"); + GLADE_HOOKUP_OBJECT (config_win, op_plugin_vbox, "op_plugin_vbox"); + GLADE_HOOKUP_OBJECT (config_win, op_plugin_optionmenu, "op_plugin_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, op_plugin_optionmenu_menu, "op_plugin_optionmenu_menu"); + GLADE_HOOKUP_OBJECT (config_win, op_plugin_optionmenu_dummy, "op_plugin_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, op_plugin_buttonbox, "op_plugin_buttonbox"); + GLADE_HOOKUP_OBJECT (config_win, op_configure_button, "op_configure_button"); + GLADE_HOOKUP_OBJECT (config_win, op_about_button, "op_about_button"); + GLADE_HOOKUP_OBJECT (config_win, label8, "label8"); + GLADE_HOOKUP_OBJECT (config_win, op_options_frame, "op_options_frame"); + GLADE_HOOKUP_OBJECT (config_win, op_options_vbox, "op_options_vbox"); + GLADE_HOOKUP_OBJECT (config_win, op_throttle_check, "op_throttle_check"); + GLADE_HOOKUP_OBJECT (config_win, op_maxblock_hbox, "op_maxblock_hbox"); + GLADE_HOOKUP_OBJECT (config_win, op_maxblock_check, "op_maxblock_check"); + GLADE_HOOKUP_OBJECT (config_win, op_maxblock_spin, "op_maxblock_spin"); + GLADE_HOOKUP_OBJECT (config_win, op_forcereopen_check, "op_forcereopen_check"); + GLADE_HOOKUP_OBJECT (config_win, label9, "label9"); + GLADE_HOOKUP_OBJECT (config_win, output_oss_label, "output_oss_label"); + GLADE_HOOKUP_OBJECT (config_win, output_null_label, "output_null_label"); + GLADE_HOOKUP_OBJECT (config_win, output_help_label, "output_help_label"); + GLADE_HOOKUP_OBJECT (config_win, config_devices_label, "config_devices_label"); + GLADE_HOOKUP_OBJECT (config_win, config_effects_page, "config_effects_page"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_frame, "ep_plugin_frame"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_vbox, "ep_plugin_vbox"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_optionmenu, "ep_plugin_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_optionmenu_menu, "ep_plugin_optionmenu_menu"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_optionmenu_dummy, "ep_plugin_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_buttonbox, "ep_plugin_buttonbox"); + GLADE_HOOKUP_OBJECT (config_win, ep_configure_button, "ep_configure_button"); + GLADE_HOOKUP_OBJECT (config_win, ep_about_button, "ep_about_button"); + GLADE_HOOKUP_OBJECT (config_win, ep_enable_check, "ep_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, ep_plugin_frame_label, "ep_plugin_frame_label"); + GLADE_HOOKUP_OBJECT (config_win, effects_volnorm_frame, "effects_volnorm_frame"); + GLADE_HOOKUP_OBJECT (config_win, effects_volnorm_table, "effects_volnorm_table"); + GLADE_HOOKUP_OBJECT (config_win, volnorm_quantaudio_check, "volnorm_quantaudio_check"); + GLADE_HOOKUP_OBJECT (config_win, volnorm_target_spin, "volnorm_target_spin"); + GLADE_HOOKUP_OBJECT (config_win, volnorm_target_hbox, "volnorm_target_hbox"); + GLADE_HOOKUP_OBJECT (config_win, volnorm_target_label, "volnorm_target_label"); + GLADE_HOOKUP_OBJECT (config_win, volnorm_enable_check, "volnorm_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, volnorm_rva2_check, "volnorm_rva2_check"); + GLADE_HOOKUP_OBJECT (config_win, label11, "label11"); + GLADE_HOOKUP_OBJECT (config_win, effects_help_label, "effects_help_label"); + GLADE_HOOKUP_OBJECT (config_win, config_effects_label, "config_effects_label"); + GLADE_HOOKUP_OBJECT (config_win, config_crossfader_page, "config_crossfader_page"); + GLADE_HOOKUP_OBJECT (config_win, xf_bufsize_hbox, "xf_bufsize_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xf_bufsize_label, "xf_bufsize_label"); + GLADE_HOOKUP_OBJECT (config_win, xf_buffer_spin, "xf_buffer_spin"); + GLADE_HOOKUP_OBJECT (config_win, xf_autobuf_check, "xf_autobuf_check"); + GLADE_HOOKUP_OBJECT (config_win, xf_config_hbox, "xf_config_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xf_config_label, "xf_config_label"); + GLADE_HOOKUP_OBJECT (config_win, xf_config_optionmenu, "xf_config_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, xf_config_optionmenu_menu, "xf_config_optionmenu_menu"); + GLADE_HOOKUP_OBJECT (config_win, xf_config_optionmenu_dummy, "xf_config_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, xf_type_hbox, "xf_type_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xf_type_label, "xf_type_label"); + GLADE_HOOKUP_OBJECT (config_win, xf_type_optionmenu, "xf_type_optionmenu"); + GLADE_HOOKUP_OBJECT (config_win, xf_type_optionmenu_menu, "xf_type_optionmenu_menu"); + GLADE_HOOKUP_OBJECT (config_win, xf_type_optionmenu_dummy, "xf_type_optionmenu_dummy"); + GLADE_HOOKUP_OBJECT (config_win, xf_type_notebook, "xf_type_notebook"); + GLADE_HOOKUP_OBJECT (config_win, xft_reopen_label, "xft_reopen_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_flush_page, "xft_flush_page"); + GLADE_HOOKUP_OBJECT (config_win, xftf_pause_frame, "xftf_pause_frame"); + GLADE_HOOKUP_OBJECT (config_win, xftf_pause_table, "xftf_pause_table"); + GLADE_HOOKUP_OBJECT (config_win, xftfp_length_label, "xftfp_length_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfp_length_spin, "xftfp_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftfp_enable_check, "xftfp_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, label12, "label12"); + GLADE_HOOKUP_OBJECT (config_win, xftf_fadein_frame, "xftf_fadein_frame"); + GLADE_HOOKUP_OBJECT (config_win, xftf_fadein_table, "xftf_fadein_table"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_length_hbox, "xftffi_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_length_label, "xftffi_length_label"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_volume_hbox, "xftffi_volume_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_volume_label, "xftffi_volume_label"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_length_spin, "xftffi_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_enable_check, "xftffi_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, xftffi_volume_spin, "xftffi_volume_spin"); + GLADE_HOOKUP_OBJECT (config_win, label13, "label13"); + GLADE_HOOKUP_OBJECT (config_win, xft_flush_label, "xft_flush_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_none_label, "xft_none_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_pause_page, "xft_pause_page"); + GLADE_HOOKUP_OBJECT (config_win, xf_pause_frame, "xf_pause_frame"); + GLADE_HOOKUP_OBJECT (config_win, xf_pause_table, "xf_pause_table"); + GLADE_HOOKUP_OBJECT (config_win, pause_length_hbox, "pause_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, pause_length_label, "pause_length_label"); + GLADE_HOOKUP_OBJECT (config_win, pause_length_spin, "pause_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, label14, "label14"); + GLADE_HOOKUP_OBJECT (config_win, xft_pause_label, "xft_pause_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_simplexf_page, "xft_simplexf_page"); + GLADE_HOOKUP_OBJECT (config_win, xf_simple_frame, "xf_simple_frame"); + GLADE_HOOKUP_OBJECT (config_win, xf_simple_table, "xf_simple_table"); + GLADE_HOOKUP_OBJECT (config_win, simple_length_hbox, "simple_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, simple_length_label, "simple_length_label"); + GLADE_HOOKUP_OBJECT (config_win, simple_length_spin, "simple_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, label15, "label15"); + GLADE_HOOKUP_OBJECT (config_win, xft_simplexf_label, "xft_simplexf_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_advancedxf_page, "xft_advancedxf_page"); + GLADE_HOOKUP_OBJECT (config_win, xf_fadeout_frame, "xf_fadeout_frame"); + GLADE_HOOKUP_OBJECT (config_win, xf_fadeout_table, "xf_fadeout_table"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_length_hbox, "fadeout_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_length_label, "fadeout_length_label"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_length_spin, "fadeout_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_volume_hbox, "fadeout_volume_hbox"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_volume_label, "fadeout_volume_label"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_volume_spin, "fadeout_volume_spin"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_options_hbox, "fadeout_options_hbox"); + GLADE_HOOKUP_OBJECT (config_win, fadeout_enable_check, "fadeout_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, label16, "label16"); + GLADE_HOOKUP_OBJECT (config_win, xf_offset_frame, "xf_offset_frame"); + GLADE_HOOKUP_OBJECT (config_win, xf_offset_table, "xf_offset_table"); + GLADE_HOOKUP_OBJECT (config_win, xfofs_custom_hbox, "xfofs_custom_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xfofs_custom_radiobutton, "xfofs_custom_radiobutton"); + GLADE_HOOKUP_OBJECT (config_win, xfofs_custom_spin, "xfofs_custom_spin"); + GLADE_HOOKUP_OBJECT (config_win, xfofs_none_radiobutton, "xfofs_none_radiobutton"); + GLADE_HOOKUP_OBJECT (config_win, xfofs_lockout_radiobutton, "xfofs_lockout_radiobutton"); + GLADE_HOOKUP_OBJECT (config_win, xfofs_lockin_radiobutton, "xfofs_lockin_radiobutton"); + GLADE_HOOKUP_OBJECT (config_win, label17, "label17"); + GLADE_HOOKUP_OBJECT (config_win, xf_fadein_frame, "xf_fadein_frame"); + GLADE_HOOKUP_OBJECT (config_win, xf_fadein_table, "xf_fadein_table"); + GLADE_HOOKUP_OBJECT (config_win, fadein_length_hbox, "fadein_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, fadein_length_label, "fadein_length_label"); + GLADE_HOOKUP_OBJECT (config_win, fadein_volume_hbox, "fadein_volume_hbox"); + GLADE_HOOKUP_OBJECT (config_win, fadein_volume_label, "fadein_volume_label"); + GLADE_HOOKUP_OBJECT (config_win, fadein_length_spin, "fadein_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, fadein_enable_check, "fadein_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, fadein_lock_check, "fadein_lock_check"); + GLADE_HOOKUP_OBJECT (config_win, fadein_volume_spin, "fadein_volume_spin"); + GLADE_HOOKUP_OBJECT (config_win, label18, "label18"); + GLADE_HOOKUP_OBJECT (config_win, xft_advancedxf_label, "xft_advancedxf_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_fadein_page, "xft_fadein_page"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_fadein_frame, "xftfi_fadein_frame"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_fadein_table, "xftfi_fadein_table"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_length_hbox, "xftfi_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_length_label, "xftfi_length_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_volume_hbox, "xftfi_volume_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_volume_label, "xftfi_volume_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_length_spin, "xftfi_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftfi_volume_spin, "xftfi_volume_spin"); + GLADE_HOOKUP_OBJECT (config_win, label19, "label19"); + GLADE_HOOKUP_OBJECT (config_win, xft_fadein_label, "xft_fadein_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_fadeout_page, "xft_fadeout_page"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_fadeout_frame, "xftfo_fadeout_frame"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_fadeout_table, "xftfo_fadeout_table"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_length_hbox, "xftfo_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_length_label, "xftfo_length_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_length_spin, "xftfo_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_volume_hbox, "xftfo_volume_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_volume_label, "xftfo_volume_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_volume_spin, "xftfo_volume_spin"); + GLADE_HOOKUP_OBJECT (config_win, label20, "label20"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_silence_frame, "xftfo_silence_frame"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_silence_table, "xftfo_silence_table"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_silence_hbox, "xftfo_silence_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_silence_label, "xftfo_silence_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfo_silence_spin, "xftfo_silence_spin"); + GLADE_HOOKUP_OBJECT (config_win, label21, "label21"); + GLADE_HOOKUP_OBJECT (config_win, xft_fadeout_label, "xft_fadeout_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_pause_none_label, "xft_pause_none_label"); + GLADE_HOOKUP_OBJECT (config_win, xft_pause_adv_page, "xft_pause_adv_page"); + GLADE_HOOKUP_OBJECT (config_win, xft_fadeoutin_frame, "xft_fadeoutin_frame"); + GLADE_HOOKUP_OBJECT (config_win, xft_fadeoutin_table, "xft_fadeoutin_table"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_fadeout_spin, "xftfoi_fadeout_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_silence_spin, "xftfoi_silence_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_fadein_spin, "xftfoi_fadein_spin"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_fadein_hbox, "xftfoi_fadein_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_fadein_label, "xftfoi_fadein_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_silence_hbox, "xftfoi_silence_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_silence_label, "xftfoi_silence_label"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_fadeout_hbox, "xftfoi_fadeout_hbox"); + GLADE_HOOKUP_OBJECT (config_win, xftfoi_fadeout_label, "xftfoi_fadeout_label"); + GLADE_HOOKUP_OBJECT (config_win, label22, "label22"); + GLADE_HOOKUP_OBJECT (config_win, xft_pause_adv_label, "xft_pause_adv_label"); + GLADE_HOOKUP_OBJECT (config_win, config_crossfade_label, "config_crossfade_label"); + GLADE_HOOKUP_OBJECT (config_win, config_gapkiller_page, "config_gapkiller_page"); + GLADE_HOOKUP_OBJECT (config_win, gap_leading_frame, "gap_leading_frame"); + GLADE_HOOKUP_OBJECT (config_win, gap_leading_table, "gap_leading_table"); + GLADE_HOOKUP_OBJECT (config_win, lgap_length_spin, "lgap_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, lgap_level_spin, "lgap_level_spin"); + GLADE_HOOKUP_OBJECT (config_win, lgap_length_hbox, "lgap_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, lgap_length_label, "lgap_length_label"); + GLADE_HOOKUP_OBJECT (config_win, lgap_level_hbox, "lgap_level_hbox"); + GLADE_HOOKUP_OBJECT (config_win, lgap_level_label, "lgap_level_label"); + GLADE_HOOKUP_OBJECT (config_win, lgap_enable_check, "lgap_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, label23, "label23"); + GLADE_HOOKUP_OBJECT (config_win, gap_trailing_frame, "gap_trailing_frame"); + GLADE_HOOKUP_OBJECT (config_win, gap_trailing_table, "gap_trailing_table"); + GLADE_HOOKUP_OBJECT (config_win, tgap_length_hbox, "tgap_length_hbox"); + GLADE_HOOKUP_OBJECT (config_win, tgap_length_label, "tgap_length_label"); + GLADE_HOOKUP_OBJECT (config_win, tgap_level_hbox, "tgap_level_hbox"); + GLADE_HOOKUP_OBJECT (config_win, tgap_level_label, "tgap_level_label"); + GLADE_HOOKUP_OBJECT (config_win, tgap_length_spin, "tgap_length_spin"); + GLADE_HOOKUP_OBJECT (config_win, tgap_level_spin, "tgap_level_spin"); + GLADE_HOOKUP_OBJECT (config_win, tgap_lock_check, "tgap_lock_check"); + GLADE_HOOKUP_OBJECT (config_win, tgap_enable_check, "tgap_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, label24, "label24"); + GLADE_HOOKUP_OBJECT (config_win, gap_advanced_frame, "gap_advanced_frame"); + GLADE_HOOKUP_OBJECT (config_win, gap_advanced_vbox, "gap_advanced_vbox"); + GLADE_HOOKUP_OBJECT (config_win, gadv_crossing_check, "gadv_crossing_check"); + GLADE_HOOKUP_OBJECT (config_win, label25, "label25"); + GLADE_HOOKUP_OBJECT (config_win, config_gapkiller_label, "config_gapkiller_label"); + GLADE_HOOKUP_OBJECT (config_win, config_misc_page, "config_misc_page"); + GLADE_HOOKUP_OBJECT (config_win, misc_debug_frame, "misc_debug_frame"); + GLADE_HOOKUP_OBJECT (config_win, misc_debug_vbox, "misc_debug_vbox"); + GLADE_HOOKUP_OBJECT (config_win, debug_stderr_check, "debug_stderr_check"); + GLADE_HOOKUP_OBJECT (config_win, debug_monitor_check, "debug_monitor_check"); + GLADE_HOOKUP_OBJECT (config_win, label26, "label26"); + GLADE_HOOKUP_OBJECT (config_win, misc_mixopt_frame, "misc_mixopt_frame"); + GLADE_HOOKUP_OBJECT (config_win, misc_mixopt_vbox, "misc_mixopt_vbox"); + GLADE_HOOKUP_OBJECT (config_win, mixopt_enable_check, "mixopt_enable_check"); + GLADE_HOOKUP_OBJECT (config_win, mixopt_reverse_check, "mixopt_reverse_check"); + GLADE_HOOKUP_OBJECT (config_win, mixopt_software_check, "mixopt_software_check"); + GLADE_HOOKUP_OBJECT (config_win, label27, "label27"); + GLADE_HOOKUP_OBJECT (config_win, misc_other_frame, "misc_other_frame"); + GLADE_HOOKUP_OBJECT (config_win, misc_other_vbox, "misc_other_vbox"); + GLADE_HOOKUP_OBJECT (config_win, moth_songchange_hbox, "moth_songchange_hbox"); + GLADE_HOOKUP_OBJECT (config_win, moth_songchange_label, "moth_songchange_label"); + GLADE_HOOKUP_OBJECT (config_win, moth_songchange_spin, "moth_songchange_spin"); + GLADE_HOOKUP_OBJECT (config_win, moth_preload_hbox, "moth_preload_hbox"); + GLADE_HOOKUP_OBJECT (config_win, moth_preload_label, "moth_preload_label"); + GLADE_HOOKUP_OBJECT (config_win, moth_preload_spin, "moth_preload_spin"); + GLADE_HOOKUP_OBJECT (config_win, moth_noxf_hbox, "moth_noxf_hbox"); + GLADE_HOOKUP_OBJECT (config_win, moth_noxf_label, "moth_noxf_label"); + GLADE_HOOKUP_OBJECT (config_win, noxf_album_check, "noxf_album_check"); + GLADE_HOOKUP_OBJECT (config_win, noxf_samefile_check, "noxf_samefile_check"); + GLADE_HOOKUP_OBJECT (config_win, moth_httpworkaround_check, "moth_httpworkaround_check"); + GLADE_HOOKUP_OBJECT (config_win, moth_opmaxused_hbox, "moth_opmaxused_hbox"); + GLADE_HOOKUP_OBJECT (config_win, moth_opmaxused_check, "moth_opmaxused_check"); + GLADE_HOOKUP_OBJECT (config_win, moth_opmaxused_spin, "moth_opmaxused_spin"); + GLADE_HOOKUP_OBJECT (config_win, moth_quantaudio_check, "moth_quantaudio_check"); + GLADE_HOOKUP_OBJECT (config_win, moth_outputkeepopened_check, "moth_outputkeepopened_check"); + GLADE_HOOKUP_OBJECT (config_win, label28, "label28"); + GLADE_HOOKUP_OBJECT (config_win, config_misc_label, "config_misc_label"); + GLADE_HOOKUP_OBJECT (config_win, config_presets_page, "config_presets_page"); + GLADE_HOOKUP_OBJECT (config_win, presets_name_hbox, "presets_name_hbox"); + GLADE_HOOKUP_OBJECT (config_win, presets_name_entry, "presets_name_entry"); + GLADE_HOOKUP_OBJECT (config_win, presets_delete_button, "presets_delete_button"); + GLADE_HOOKUP_OBJECT (config_win, presets_new_button, "presets_new_button"); + GLADE_HOOKUP_OBJECT (config_win, presets_list_scrolledwindow, "presets_list_scrolledwindow"); + GLADE_HOOKUP_OBJECT (config_win, presets_list_list, "presets_list_list"); + GLADE_HOOKUP_OBJECT (config_win, presets_list_bbox, "presets_list_bbox"); + GLADE_HOOKUP_OBJECT (config_win, presets_load_button, "presets_load_button"); + GLADE_HOOKUP_OBJECT (config_win, presets_save_button, "presets_save_button"); + GLADE_HOOKUP_OBJECT (config_win, config_presets_label, "config_presets_label"); + GLADE_HOOKUP_OBJECT (config_win, config_bbox, "config_bbox"); + GLADE_HOOKUP_OBJECT (config_win, config_ok, "config_ok"); + GLADE_HOOKUP_OBJECT (config_win, config_cancel, "config_cancel"); + GLADE_HOOKUP_OBJECT (config_win, config_apply, "config_apply"); + GLADE_HOOKUP_OBJECT_NO_REF (config_win, tooltips, "tooltips"); + + return config_win; +} + +GtkWidget* +create_monitor_win (void) +{ + GtkWidget *monitor_win; + GtkWidget *monitor_table; + GtkWidget *monitor_output_hbox; + GtkWidget *monitor_output_progress; + GtkWidget *monitor_output_ms_label; + GtkWidget *monitor_displaylabel_hbox; + GtkWidget *monitor_display_label; + GtkWidget *monitor_position_hbox; + GtkWidget *monpos_position_label; + GtkWidget *monpos_label1; + GtkWidget *monpos_total_label; + GtkWidget *monpos_label2; + GtkWidget *monpos_left_label; + GtkWidget *monpos_label3; + GtkWidget *monpos_written_time_label; + GtkWidget *monpos_output_time_separator_label; + GtkWidget *monpos_output_time_label; + GtkWidget *monitor_outputlabel_hbox; + GtkWidget *monitor_output_label; + GtkWidget *monitor_positionlabel_hbox; + GtkWidget *monitor_position_label; + GtkWidget *monitor_display_hbox; + GtkWidget *monitor_display_frame; + GtkWidget *monitor_display_drawingarea; + GtkWidget *monitor_display_space_label; + GtkWidget *monitor_button_hbox; + GtkWidget *monitor_seekeof_button; + GtkWidget *alignment1; + GtkWidget *hbox2; + GtkWidget *image1; + GtkWidget *label29; + + monitor_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (monitor_win), "Crossfade Buffer Monitor"); + gtk_window_set_default_size (GTK_WINDOW (monitor_win), 320, -1); + + monitor_table = gtk_table_new (4, 2, FALSE); + gtk_widget_show (monitor_table); + gtk_container_add (GTK_CONTAINER (monitor_win), monitor_table); + gtk_container_set_border_width (GTK_CONTAINER (monitor_table), 5); + gtk_table_set_row_spacings (GTK_TABLE (monitor_table), 3); + gtk_table_set_col_spacings (GTK_TABLE (monitor_table), 5); + + monitor_output_hbox = gtk_hbox_new (FALSE, 3); + gtk_widget_show (monitor_output_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_output_hbox, 1, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + monitor_output_progress = gtk_progress_bar_new (); + gtk_widget_show (monitor_output_progress); + gtk_box_pack_start (GTK_BOX (monitor_output_hbox), monitor_output_progress, TRUE, TRUE, 0); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (monitor_output_progress), " "); + + monitor_output_ms_label = gtk_label_new ("ms"); + gtk_widget_show (monitor_output_ms_label); + gtk_box_pack_start (GTK_BOX (monitor_output_hbox), monitor_output_ms_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monitor_output_ms_label), GTK_JUSTIFY_CENTER); + + monitor_displaylabel_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (monitor_displaylabel_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_displaylabel_hbox, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + monitor_display_label = gtk_label_new ("Mixing Buffer:"); + gtk_widget_show (monitor_display_label); + gtk_box_pack_end (GTK_BOX (monitor_displaylabel_hbox), monitor_display_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monitor_display_label), GTK_JUSTIFY_CENTER); + + monitor_position_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (monitor_position_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_position_hbox, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + monpos_position_label = gtk_label_new ("-:--.-"); + gtk_widget_show (monpos_position_label); + gtk_box_pack_start (GTK_BOX (monitor_position_hbox), monpos_position_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_position_label), GTK_JUSTIFY_CENTER); + + monpos_label1 = gtk_label_new (" / "); + gtk_widget_show (monpos_label1); + gtk_box_pack_start (GTK_BOX (monitor_position_hbox), monpos_label1, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_label1), GTK_JUSTIFY_CENTER); + + monpos_total_label = gtk_label_new ("-:--"); + gtk_widget_show (monpos_total_label); + gtk_box_pack_start (GTK_BOX (monitor_position_hbox), monpos_total_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_total_label), GTK_JUSTIFY_CENTER); + + monpos_label2 = gtk_label_new (" total, "); + gtk_widget_show (monpos_label2); + gtk_box_pack_start (GTK_BOX (monitor_position_hbox), monpos_label2, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_label2), GTK_JUSTIFY_CENTER); + + monpos_left_label = gtk_label_new ("-:--"); + gtk_widget_show (monpos_left_label); + gtk_box_pack_start (GTK_BOX (monitor_position_hbox), monpos_left_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_left_label), GTK_JUSTIFY_CENTER); + + monpos_label3 = gtk_label_new (" left"); + gtk_widget_show (monpos_label3); + gtk_box_pack_start (GTK_BOX (monitor_position_hbox), monpos_label3, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_label3), GTK_JUSTIFY_CENTER); + + monpos_written_time_label = gtk_label_new ("-:--:--.-"); + gtk_widget_show (monpos_written_time_label); + gtk_box_pack_end (GTK_BOX (monitor_position_hbox), monpos_written_time_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_written_time_label), GTK_JUSTIFY_CENTER); + + monpos_output_time_separator_label = gtk_label_new (" / "); + gtk_widget_show (monpos_output_time_separator_label); + gtk_box_pack_end (GTK_BOX (monitor_position_hbox), monpos_output_time_separator_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_output_time_separator_label), GTK_JUSTIFY_CENTER); + + monpos_output_time_label = gtk_label_new ("-:--.---"); + gtk_widget_show (monpos_output_time_label); + gtk_box_pack_end (GTK_BOX (monitor_position_hbox), monpos_output_time_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monpos_output_time_label), GTK_JUSTIFY_CENTER); + + monitor_outputlabel_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (monitor_outputlabel_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_outputlabel_hbox, 0, 1, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + monitor_output_label = gtk_label_new ("Output Buffer:"); + gtk_widget_show (monitor_output_label); + gtk_box_pack_end (GTK_BOX (monitor_outputlabel_hbox), monitor_output_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monitor_output_label), GTK_JUSTIFY_CENTER); + + monitor_positionlabel_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (monitor_positionlabel_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_positionlabel_hbox, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + monitor_position_label = gtk_label_new ("Position:"); + gtk_widget_show (monitor_position_label); + gtk_box_pack_end (GTK_BOX (monitor_positionlabel_hbox), monitor_position_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (monitor_position_label), GTK_JUSTIFY_CENTER); + + monitor_display_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (monitor_display_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_display_hbox, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), 0, 0); + + monitor_display_frame = gtk_frame_new (NULL); + gtk_widget_show (monitor_display_frame); + gtk_box_pack_start (GTK_BOX (monitor_display_hbox), monitor_display_frame, TRUE, TRUE, 0); + + monitor_display_drawingarea = gtk_drawing_area_new (); + gtk_widget_show (monitor_display_drawingarea); + gtk_container_add (GTK_CONTAINER (monitor_display_frame), monitor_display_drawingarea); + gtk_widget_set_size_request (monitor_display_drawingarea, -1, 20); + + monitor_display_space_label = gtk_label_new (""); + gtk_widget_show (monitor_display_space_label); + gtk_box_pack_start (GTK_BOX (monitor_display_hbox), monitor_display_space_label, FALSE, FALSE, 0); + + monitor_button_hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (monitor_button_hbox); + gtk_table_attach (GTK_TABLE (monitor_table), monitor_button_hbox, 1, 2, 3, 4, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (GTK_FILL), 0, 0); + + monitor_seekeof_button = gtk_button_new (); + gtk_widget_show (monitor_seekeof_button); + gtk_box_pack_start (GTK_BOX (monitor_button_hbox), monitor_seekeof_button, FALSE, FALSE, 0); + + alignment1 = gtk_alignment_new (0.5, 0.5, 0, 0); + gtk_widget_show (alignment1); + gtk_container_add (GTK_CONTAINER (monitor_seekeof_button), alignment1); + + hbox2 = gtk_hbox_new (FALSE, 2); + gtk_widget_show (hbox2); + gtk_container_add (GTK_CONTAINER (alignment1), hbox2); + + image1 = gtk_image_new_from_stock ("gtk-media-next", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image1); + gtk_box_pack_start (GTK_BOX (hbox2), image1, FALSE, FALSE, 0); + + label29 = gtk_label_new_with_mnemonic ("Seek to EOF"); + gtk_widget_show (label29); + gtk_box_pack_start (GTK_BOX (hbox2), label29, FALSE, FALSE, 0); + + g_signal_connect ((gpointer) monitor_win, "delete_event", + G_CALLBACK (on_monitor_win_delete_event), + NULL); + g_signal_connect ((gpointer) monitor_display_drawingarea, "expose_event", + G_CALLBACK (on_monitor_display_drawingarea_expose_event), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (monitor_win, monitor_win, "monitor_win"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_table, "monitor_table"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_output_hbox, "monitor_output_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_output_progress, "monitor_output_progress"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_output_ms_label, "monitor_output_ms_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_displaylabel_hbox, "monitor_displaylabel_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_display_label, "monitor_display_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_position_hbox, "monitor_position_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_position_label, "monpos_position_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_label1, "monpos_label1"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_total_label, "monpos_total_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_label2, "monpos_label2"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_left_label, "monpos_left_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_label3, "monpos_label3"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_written_time_label, "monpos_written_time_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_output_time_separator_label, "monpos_output_time_separator_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monpos_output_time_label, "monpos_output_time_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_outputlabel_hbox, "monitor_outputlabel_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_output_label, "monitor_output_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_positionlabel_hbox, "monitor_positionlabel_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_position_label, "monitor_position_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_display_hbox, "monitor_display_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_display_frame, "monitor_display_frame"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_display_drawingarea, "monitor_display_drawingarea"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_display_space_label, "monitor_display_space_label"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_button_hbox, "monitor_button_hbox"); + GLADE_HOOKUP_OBJECT (monitor_win, monitor_seekeof_button, "monitor_seekeof_button"); + GLADE_HOOKUP_OBJECT (monitor_win, alignment1, "alignment1"); + GLADE_HOOKUP_OBJECT (monitor_win, hbox2, "hbox2"); + GLADE_HOOKUP_OBJECT (monitor_win, image1, "image1"); + GLADE_HOOKUP_OBJECT (monitor_win, label29, "label29"); + + return monitor_win; +} + +GtkWidget* +create_about_win (void) +{ + GtkWidget *about_win; + GtkWidget *about_vbox; + GtkWidget *about_label; + GtkWidget *about_actionarea; + GtkWidget *about_ok; + + about_win = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (about_win), "About XMMS Crossfade Plugin"); + gtk_window_set_type_hint (GTK_WINDOW (about_win), GDK_WINDOW_TYPE_HINT_DIALOG); + + about_vbox = GTK_DIALOG (about_win)->vbox; + gtk_widget_show (about_vbox); + + about_label = gtk_label_new ("dummy"); + gtk_widget_show (about_label); + gtk_box_pack_start (GTK_BOX (about_vbox), about_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (about_label), GTK_JUSTIFY_CENTER); + + about_actionarea = GTK_DIALOG (about_win)->action_area; + gtk_widget_show (about_actionarea); + gtk_button_box_set_layout (GTK_BUTTON_BOX (about_actionarea), GTK_BUTTONBOX_END); + + about_ok = gtk_button_new_with_mnemonic ("OK"); + gtk_widget_show (about_ok); + gtk_dialog_add_action_widget (GTK_DIALOG (about_win), about_ok, 0); + + g_signal_connect_swapped ((gpointer) about_ok, "clicked", + G_CALLBACK (gtk_widget_destroy), + GTK_OBJECT (about_win)); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (about_win, about_win, "about_win"); + GLADE_HOOKUP_OBJECT_NO_REF (about_win, about_vbox, "about_vbox"); + GLADE_HOOKUP_OBJECT (about_win, about_label, "about_label"); + GLADE_HOOKUP_OBJECT_NO_REF (about_win, about_actionarea, "about_actionarea"); + GLADE_HOOKUP_OBJECT (about_win, about_ok, "about_ok"); + + return about_win; +} + +GtkWidget* +create_help_win (void) +{ + GtkWidget *help_win; + GtkWidget *help_vbox; + GtkWidget *help_scrolledwindow; + GtkWidget *help_text; + GtkWidget *help_buttonbox; + GtkWidget *help_close_button; + + help_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (help_win), "Help"); + + help_vbox = gtk_vbox_new (FALSE, 0); + gtk_widget_show (help_vbox); + gtk_container_add (GTK_CONTAINER (help_win), help_vbox); + gtk_container_set_border_width (GTK_CONTAINER (help_vbox), 5); + + help_scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (help_scrolledwindow); + gtk_box_pack_start (GTK_BOX (help_vbox), help_scrolledwindow, TRUE, TRUE, 0); + GTK_WIDGET_UNSET_FLAGS (help_scrolledwindow, GTK_CAN_FOCUS); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (help_scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (help_scrolledwindow), GTK_SHADOW_IN); + + help_text = gtk_text_view_new (); + gtk_widget_show (help_text); + gtk_container_add (GTK_CONTAINER (help_scrolledwindow), help_text); + gtk_text_view_set_editable (GTK_TEXT_VIEW (help_text), FALSE); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (help_text), GTK_WRAP_WORD); + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (help_text)), "<dummy>\n", -1); + + help_buttonbox = gtk_hbutton_box_new (); + gtk_widget_show (help_buttonbox); + gtk_box_pack_start (GTK_BOX (help_vbox), help_buttonbox, FALSE, FALSE, 0); + gtk_button_box_set_layout (GTK_BUTTON_BOX (help_buttonbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing (GTK_BOX (help_buttonbox), 30); + + help_close_button = gtk_button_new_with_mnemonic ("Close"); + gtk_widget_show (help_close_button); + gtk_container_add (GTK_CONTAINER (help_buttonbox), help_close_button); + GTK_WIDGET_SET_FLAGS (help_close_button, GTK_CAN_DEFAULT); + + g_signal_connect ((gpointer) help_close_button, "clicked", + G_CALLBACK (on_help_close_button_clicked), + NULL); + + /* Store pointers to all widgets, for use by lookup_widget(). */ + GLADE_HOOKUP_OBJECT_NO_REF (help_win, help_win, "help_win"); + GLADE_HOOKUP_OBJECT (help_win, help_vbox, "help_vbox"); + GLADE_HOOKUP_OBJECT (help_win, help_scrolledwindow, "help_scrolledwindow"); + GLADE_HOOKUP_OBJECT (help_win, help_text, "help_text"); + GLADE_HOOKUP_OBJECT (help_win, help_buttonbox, "help_buttonbox"); + GLADE_HOOKUP_OBJECT (help_win, help_close_button, "help_close_button"); + + return help_win; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/interface-2.0.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,8 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +GtkWidget* create_config_win (void); +GtkWidget* create_monitor_win (void); +GtkWidget* create_about_win (void); +GtkWidget* create_help_win (void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/monitor.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,460 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <gtk/gtk.h> + +#include "monitor.h" +#include "configure.h" +#include "cfgutil.h" +#include "crossfade.h" + +#include "interface-2.0.h" +#include "support-2.0.h" + +extern MUTEX buffer_mutex; + +GtkWidget *monitor_win = NULL; +GtkWidget *monitor_display_drawingarea; +GtkEntry *monitor_output_entry; +GtkProgress *monitor_output_progress; + +static GtkLabel *monitor_position_label; +static GtkLabel *monitor_total_label; +static GtkLabel *monitor_left_label; +static GtkLabel *monitor_output_time_label; +static GtkLabel *monitor_output_time_sep; +static GtkLabel *monitor_written_time_label; + +static gchar *default_position_str = NULL; +static gchar *default_total_str = NULL; +static gchar *default_left_str = NULL; +static gchar *default_output_time_str = NULL; +static gchar *default_written_time_str = NULL; + +static gboolean monitor_active = FALSE; +static guint monitor_tag; +static gint monitor_output_max; +static gint monitor_closing; + +#define RUNNING 0 +#define CLOSING 1 +#define CLOSED 2 + +#define SMOD(x,n) (((x)<0)?((n)-(x))%(n):((x)%(n))) + + +static void +draw_wrapped(GtkWidget * widget, gint pos, gint width, GdkGC * gc) +{ + GdkDrawable *win = widget->window; + + gint ww = widget->allocation.width; + gint wh = widget->allocation.height; + + if (width <= 0) + return; + + if (width < ww) + { + gint x = SMOD(pos, ww); + if ((x + width) >= ww) + { + gdk_draw_rectangle(win, gc, TRUE, x, 0, ww - x, wh); + gdk_draw_rectangle(win, gc, TRUE, 0, 0, width - (ww - x), wh); + } + else + gdk_draw_rectangle(win, gc, TRUE, x, 0, width, wh); + } + else + gdk_draw_rectangle(win, gc, TRUE, 0, 0, ww, wh); +} + +gboolean +on_monitor_display_drawingarea_expose_event(GtkWidget * widget, GdkEventExpose * event, gpointer user_data) +{ + if (buffer && buffer->size && output_opened) + { + gint ww = widget->allocation.width; + + gint x1 = 0; + gint x2 = buffer->used; + gint x3 = buffer->used + buffer->mix; + gint x4 = buffer->size; + + x1 = (gint64) (x1 + buffer->rd_index) * ww / buffer->size; + x2 = (gint64) (x2 + buffer->rd_index) * ww / buffer->size; + x3 = (gint64) (x3 + buffer->rd_index) * ww / buffer->size; + x4 = (gint64) (x4 + buffer->rd_index) * ww / buffer->size; + + draw_wrapped(widget, x1, x2 - x1, widget->style->fg_gc[GTK_STATE_NORMAL]); + draw_wrapped(widget, x2, x3 - x2, widget->style->white_gc); + draw_wrapped(widget, x3, x4 - x3, widget->style->bg_gc[GTK_STATE_NORMAL]); + } + else + gdk_window_clear_area(widget->window, event->area.x, event->area.y, event->area.width, event->area.height); + + return TRUE; +} + +gboolean +on_monitor_win_delete_event(GtkWidget * widget, GdkEvent * event, gpointer user_data) +{ + /* V0.1.1 20000618: removed, didn't make much sense since it wasn't saved */ + /* if (config) config->enable_monitor = FALSE; */ + if (default_position_str) + { + g_free(default_position_str); + default_position_str = NULL; + } + if (default_total_str) + { + g_free(default_total_str); + default_total_str = NULL; + } + if (default_left_str) + { + g_free(default_left_str); + default_left_str = NULL; + } + if (default_output_time_str) + { + g_free(default_output_time_str); + default_output_time_str = NULL; + } + if (default_written_time_str) + { + g_free(default_written_time_str); + default_written_time_str = NULL; + } + return (FALSE); /* FALSE: destroy window */ +} + +void +xfade_check_monitor_win() +{ + gchar *str; + + if (config->enable_monitor) + { + if (!monitor_win && !(monitor_win = create_monitor_win())) + { + DEBUG(("[crossfade] check_monitor_win: error creating window!\n")); + return; + } +#if 0 + if (!GDK_IS_WINDOW(monitor_win)) + { + DEBUG(("[crossfade] check_monitor_win: GDK_IS_WINDOW(monitor_win) failed!\n")); + DEBUG(("[crossfade] check_monitor_win: probably running in headless mode!\n")); + monitor_win = NULL; + return; + } +#endif + /* automatically set monitor_win to NULL when window is destroyed */ + gtk_signal_connect(GTK_OBJECT(monitor_win), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &monitor_win); + + /* show window */ + gtk_widget_hide(GTK_WIDGET(lookup_widget(monitor_win, "monitor_seekeof_button"))); + gtk_widget_show(monitor_win); + + /* get pointers to widgets (used by crossfade.c to update the monitor) */ + monitor_display_drawingarea = lookup_widget(monitor_win, "monitor_display_drawingarea"); + monitor_output_progress = GTK_PROGRESS(lookup_widget(monitor_win, "monitor_output_progress")); + monitor_position_label = GTK_LABEL (lookup_widget(monitor_win, "monpos_position_label")); + monitor_total_label = GTK_LABEL (lookup_widget(monitor_win, "monpos_total_label")); + monitor_left_label = GTK_LABEL (lookup_widget(monitor_win, "monpos_left_label")); + monitor_output_time_label = GTK_LABEL (lookup_widget(monitor_win, "monpos_output_time_label")); + monitor_output_time_sep = GTK_LABEL (lookup_widget(monitor_win, "monpos_output_time_separator_label")); + monitor_written_time_label = GTK_LABEL (lookup_widget(monitor_win, "monpos_written_time_label")); + + /* get default strings (displayed when monitor is stopped) */ + if (!default_position_str) + { + gtk_label_get(monitor_position_label, &str); + default_position_str = g_strdup(str); + } + if (!default_total_str) + { + gtk_label_get(monitor_total_label, &str); + default_total_str = g_strdup(str); + } + if (!default_left_str) + { + gtk_label_get(monitor_left_label, &str); + default_left_str = g_strdup(str); + } + if (!default_output_time_str) + { + gtk_label_get(monitor_output_time_label, &str); + default_output_time_str = g_strdup(str); + } + if (!default_written_time_str) + { + gtk_label_get(monitor_written_time_label, &str); + default_written_time_str = g_strdup(str); + } + + /* force gtk_progress_configure */ + monitor_output_max = 0; + } + else if (monitor_win) + gtk_widget_destroy(monitor_win); +} + +void +label_set_text(GtkLabel * label, gchar * text) +{ + gchar *old_text; + gtk_label_get(label, &old_text); + if (strcmp(old_text, text) == 0) + return; + gtk_label_set_text(label, text); +} + +gint +xfade_update_monitor(gpointer userdata) +{ + GdkRectangle update_rect; + + /* HACK: (see xfade_stop_monitor() below) */ + if (monitor_closing == CLOSED) + return TRUE; + + if (monitor_closing == CLOSING) + monitor_closing = CLOSED; + + if (!monitor_win) + return TRUE; + + /* lock buffer (only if we need to) */ + if (monitor_closing != CLOSED) + MUTEX_LOCK(&buffer_mutex); + + gint output_time = the_op->output_time(); + gint written_time = the_op->written_time(); + gint output_used = written_time - output_time; + + /*** Mixing Buffer ***/ + update_rect.x = 0; + update_rect.y = 0; + update_rect.width = monitor_display_drawingarea->allocation.width; + update_rect.height = monitor_display_drawingarea->allocation.height; + + if (monitor_closing == CLOSED) + gdk_window_clear_area(monitor_display_drawingarea->window, + update_rect.x, update_rect.y, + update_rect.width, update_rect.height); + else + gtk_widget_draw(monitor_display_drawingarea, &update_rect); + + /*** Output Buffer ***/ + if (monitor_closing == CLOSED) + { + gtk_progress_configure(monitor_output_progress, 0, 0, 0); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(monitor_output_progress), " "); + monitor_output_max = 0; + } + else + { + if (output_opened && the_op->buffer_playing()) + { + if (output_used < 0) + output_used = 0; + + if (output_used > monitor_output_max) + { + monitor_output_max = output_used; + gtk_progress_configure(monitor_output_progress, + output_used, 0, monitor_output_max); + } + else + gtk_progress_set_value(monitor_output_progress, output_used); + + { + gchar temp[32]; + g_snprintf(temp, sizeof(temp), "%d", output_used); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(monitor_output_progress), temp); + } + } + else + { + gtk_progress_configure(monitor_output_progress, 0, 0, 0); + gtk_progress_bar_set_text(GTK_PROGRESS_BAR(monitor_output_progress), " "); + monitor_output_max = 0; + } + } + + /*** Position ***/ + if (!xfplayer_input_playing() || (monitor_closing == CLOSED)) + { + gtk_label_set_text(monitor_position_label, default_position_str); + gtk_label_set_text(monitor_total_label, default_total_str); + gtk_label_set_text(monitor_left_label, default_left_str); + } + else + { + gchar buffer[32]; + gint position = output_time - output_offset; + gint total = xfplaylist_current_length(); + gint left = total - position; + + /* position */ + g_snprintf(buffer, sizeof(buffer), + position < 0 ? "-%d:%02d.%01d" : "%d:%02d.%01d", + ABS(position) / 60000, + ABS(position) / 1000 % 60, + ABS(position) / 100 % 10); + gtk_label_set_text(monitor_position_label, buffer); + + /* total */ + if (total > 0) + { + g_snprintf(buffer, sizeof(buffer), + "%d:%02d", + total / 60000, + total / 1000 % 60); + gtk_label_set_text(monitor_total_label, buffer); + } + else + label_set_text(monitor_total_label, default_total_str); + + /* left */ + if (total > 0) + { + g_snprintf(buffer, sizeof(buffer), + left < 0 ? "-%d:%02d" : "%d:%02d", + ABS(left) / 60000, + ABS(left) / 1000 % 60); + gtk_label_set_text(monitor_left_label, buffer); + } + else + label_set_text(monitor_left_label, default_left_str); + } + + + /* Output Plugin position */ + if (monitor_closing == CLOSED) + { + gtk_widget_hide(GTK_WIDGET(monitor_output_time_label)); + gtk_widget_hide(GTK_WIDGET(monitor_output_time_sep)); + gtk_label_set_text(monitor_written_time_label, default_written_time_str); + } + else + { + gchar buffer[32]; + + /* check for output plugin bug -- diff should always be 0 */ + gint diff = written_time - (gint) (output_streampos * 1000 / OUTPUT_BPS); + if (diff) + { + gtk_widget_show(GTK_WIDGET(monitor_output_time_label)); + gtk_widget_show(GTK_WIDGET(monitor_output_time_sep)); + g_snprintf(buffer, sizeof(buffer), + output_time < 0 ? "-%d:%02d.%03d" : "%d:%02d.%03d", + ABS(diff) / 60000, + ABS(diff) / 1000 % 60, + ABS(diff) % 1000); + gtk_label_set_text(monitor_output_time_label, buffer); + } + else + { + gtk_widget_hide(GTK_WIDGET(monitor_output_time_label)); + gtk_widget_hide(GTK_WIDGET(monitor_output_time_sep)); + } + + /* written_time */ + g_snprintf(buffer, sizeof(buffer), + written_time < 0 ? "-%d:%02d:%02d.%01d" : "%d:%02d:%02d.%01d", + ABS(written_time) / 3600000, + ABS(written_time) / 60000 % 60, + ABS(written_time) / 1000 % 60, + ABS(written_time) / 100 % 10); + gtk_label_set_text(monitor_written_time_label, buffer); + } + + /* unlock buffer */ + if (monitor_closing != CLOSED) + MUTEX_UNLOCK(&buffer_mutex); + + return TRUE; /* continue calling this function */ +} + +void +xfade_start_monitor() +{ + if (monitor_active) + return; + + monitor_output_max = 0; + monitor_closing = RUNNING; + monitor_active = TRUE; + monitor_tag = gtk_timeout_add(UPDATE_INTERVAL, xfade_update_monitor, NULL); +} + +void +xfade_stop_monitor() +{ + gint max_wait = UPDATE_INTERVAL / 10 + 1 + 1; /* round up / add safety */ + + if (!monitor_active) + return; + + /* HACK, ugly HACK: force a final call of xfade_update_monitor */ + monitor_closing = CLOSING; + while ((monitor_closing == CLOSING) && (max_wait-- > 0)) + xfade_usleep(10000); + + if (max_wait <= 0) + DEBUG(("[crossfade] stop_monitor: timeout!\n")); + + /* stop calling xfade_update_monitor() */ + gtk_timeout_remove(monitor_tag); + monitor_active = FALSE; +} + +#if defined(HAVE_INPUT_SEEK) +void input_seek(int time); /* XMMS */ +void +on_monitor_seekeof_button_clicked(GtkButton *button, gpointer user_data) +{ + gint total = xfplaylist_current_length(); + gint offset = xfade_cfg_offset(&config->fc[FADE_CONFIG_XFADE]) + - config->songchange_timeout; + gint position = total + offset - 2500; + + if (position < 0) + return; + + DEBUG(("[crossfade] monitor_seek_eof: total=%d offset=%d position=%d\n", total, offset, position)) + + input_seek(position/1000); /* XMMS */ +} +#else +void on_monitor_seekeof_button_clicked(GtkButton *button, gpointer user_data) +{ } +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/monitor.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,42 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef _MONITOR_H_ +#define _MONITOR_H_ + +#include <glib.h> + +/* time between updates in ms (default: 33 -> 30 updates per second) */ +#define UPDATE_INTERVAL 33 + +void xfade_check_monitor_win(); +gint xfade_update_monitor(gpointer userdata); +void xfade_start_monitor(); +void xfade_stop_monitor(); + +extern GtkWidget *monitor_win; +extern GtkWidget *monitor_display_drawingarea; +extern GtkEntry *monitor_output_entry; +extern GtkProgress *monitor_output_progress; + +#endif /* _MONITOR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/player.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,162 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#include <audacious/plugin.h> +#include "player.h" + +#if defined(COMPILE_FOR_XMMS) /***********************************************/ + +gboolean get_input_playing(); +gboolean xfplayer_input_playing() { + return get_input_playing(); +} + +gint get_playlist_position(); +gint xfplaylist_get_position() { + return get_playlist_position(); +} + +gchar *playlist_get_filename(gint pos); +gchar *xfplaylist_get_filename(gint pos) { + return playlist_get_filename(pos); +} + +gchar *playlist_get_songtitle(gint pos); +gchar *xfplaylist_get_songtitle(gint pos) { + return playlist_get_songtitle(pos); +} + +gint playlist_get_current_length(); +gint xfplaylist_current_length() { + return playlist_get_current_length(); +} + +GList *get_output_list(); +GList *xfplayer_get_output_list () { + return get_output_list(); +} + +GList *get_effect_list(); +GList *xfplayer_get_effect_list() { + return get_effect_list(); +} + +gboolean xfplayer_effects_enabled() { + return effects_enabled(); +} + +EffectPlugin *xfplayer_get_current_effect_plugin() { + return get_current_effect_plugin(); +} + +gboolean xfplayer_check_realtime_priority() { + return xmms_check_realtime_priority(); +} + +#elif defined(COMPILE_FOR_AUDACIOUS) /****************************************/ +#if AUDACIOUS_ABI_VERSION >= 2 /*--------------------------------------------*/ + +gboolean xfplayer_input_playing() { + return audacious_drct_get_playing(); +} + +gint xfplaylist_get_position() { + Playlist *playlist = aud_playlist_get_active(); + return aud_playlist_get_position(playlist); +} + +gchar *xfplaylist_get_filename(gint pos) { + Playlist *playlist = aud_playlist_get_active(); + char *uri = aud_playlist_get_filename(playlist, pos); + return g_filename_from_uri(uri, NULL, NULL); +} + +gchar *xfplaylist_get_songtitle(gint pos) { + Playlist *playlist = aud_playlist_get_active(); + return aud_playlist_get_songtitle(playlist, pos); +} + +gint xfplaylist_current_length() { + Playlist *playlist = aud_playlist_get_active(); + return aud_playlist_get_current_length(playlist); +} + +GList *xfplayer_get_output_list() { + return aud_get_output_list(); +} + +#else /*---------------------------------------------------------------------*/ + +#if AUDACIOUS_ABI_VERSION >= 1 +gboolean playback_get_playing(); /* >= Audacious-1.3.0 */ +gboolean xfplayer_input_playing() { + return playback_get_playing(); +} +#else +gboolean bmp_playback_get_playing(); /* Audacious (old) */ +gboolean xfplayer_input_playing() { + return bmp_playback_get_playing(); +} +#endif + +gint xfplaylist_get_position() { + return playlist_get_position(playlist_get_active()); +} + +gchar *xfplaylist_get_filename(gint pos) { + char *uri = playlist_get_filename(playlist_get_active(), pos); + return g_filename_from_uri(uri, NULL, NULL); +} + +gchar *xfplaylist_get_songtitle(gint pos) { + return playlist_get_songtitle(playlist_get_active(), pos); +} + +gint xfplaylist_current_length() { + return playlist_get_current_length(playlist_get_active()); +} + +GList *get_output_list(); +GList *xfplayer_get_output_list() { + return get_output_list(); +} + +#endif /*--------------------------------------------------------------------*/ + +GList *xfplayer_get_effect_list() { + return NULL; +} + +EffectPlugin *xfplayer_get_current_effect_plugin() { + return NULL; +} + +gboolean xfplayer_effects_enabled() { + return FALSE; +} + +gboolean xfplayer_check_realtime_priority() { + return FALSE; +} + +#endif /**********************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/player.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,44 @@ +/* + * XMMS Crossfade Plugin + * Copyright (C) 2000-2007 Peter Eisenlohr <peter@eisenlohr.org> + * + * based on the original OSS Output Plugin + * 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. + */ + +#ifndef _PLAYER_H_ +#define _PLAYER_H_ + +#include "crossfade.h" + +gboolean xfplayer_input_playing(); + +gint xfplaylist_get_position(); +gchar *xfplaylist_get_filename (gint pos); +gchar *xfplaylist_get_songtitle(gint pos); +gint xfplaylist_current_length(); + +GList *xfplayer_get_output_list(); + +GList *xfplayer_get_effect_list(); +EffectPlugin *xfplayer_get_current_effect_plugin(); +gboolean xfplayer_effects_enabled(); + +gboolean xfplayer_check_realtime_priority(); + +#endif /* _PLAYER_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/support-2.0.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,144 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#include <gtk/gtk.h> + +#include "support-2.0.h" + +GtkWidget* +lookup_widget (GtkWidget *widget, + const gchar *widget_name) +{ + GtkWidget *parent, *found_widget; + + for (;;) + { + if (GTK_IS_MENU (widget)) + parent = gtk_menu_get_attach_widget (GTK_MENU (widget)); + else + parent = widget->parent; + if (!parent) + parent = (GtkWidget*) g_object_get_data (G_OBJECT (widget), "GladeParentKey"); + if (parent == NULL) + break; + widget = parent; + } + + found_widget = (GtkWidget*) g_object_get_data (G_OBJECT (widget), + widget_name); + if (!found_widget) + g_warning ("Widget not found: %s", widget_name); + return found_widget; +} + +static GList *pixmaps_directories = NULL; + +/* Use this function to set the directory containing installed pixmaps. */ +void +add_pixmap_directory (const gchar *directory) +{ + pixmaps_directories = g_list_prepend (pixmaps_directories, + g_strdup (directory)); +} + +/* This is an internally used function to find pixmap files. */ +static gchar* +find_pixmap_file (const gchar *filename) +{ + GList *elem; + + /* We step through each of the pixmaps directory to find it. */ + elem = pixmaps_directories; + while (elem) + { + gchar *pathname = g_strdup_printf ("%s%s%s", (gchar*)elem->data, + G_DIR_SEPARATOR_S, filename); + if (g_file_test (pathname, G_FILE_TEST_EXISTS)) + return pathname; + g_free (pathname); + elem = elem->next; + } + return NULL; +} + +/* This is an internally used function to create pixmaps. */ +GtkWidget* +create_pixmap (GtkWidget *widget, + const gchar *filename) +{ + gchar *pathname = NULL; + GtkWidget *pixmap; + + if (!filename || !filename[0]) + return gtk_image_new (); + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning ("Couldn't find pixmap file: %s", filename); + return gtk_image_new (); + } + + pixmap = gtk_image_new_from_file (pathname); + g_free (pathname); + return pixmap; +} + +/* This is an internally used function to create pixmaps. */ +GdkPixbuf* +create_pixbuf (const gchar *filename) +{ + gchar *pathname = NULL; + GdkPixbuf *pixbuf; + GError *error = NULL; + + if (!filename || !filename[0]) + return NULL; + + pathname = find_pixmap_file (filename); + + if (!pathname) + { + g_warning ("Couldn't find pixmap file: %s", filename); + return NULL; + } + + pixbuf = gdk_pixbuf_new_from_file (pathname, &error); + if (!pixbuf) + { + fprintf (stderr, "Failed to load pixbuf file: %s: %s\n", + pathname, error->message); + g_error_free (error); + } + g_free (pathname); + return pixbuf; +} + +/* This is used to set ATK action descriptions. */ +void +glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description) +{ + gint n_actions, i; + + n_actions = atk_action_get_n_actions (action); + for (i = 0; i < n_actions; i++) + { + if (!strcmp (atk_action_get_name (action, i), action_name)) + atk_action_set_description (action, i, description); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/support-2.0.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,44 @@ +/* + * DO NOT EDIT THIS FILE - it is generated by Glade. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gtk/gtk.h> + +/* + * Public Functions. + */ + +/* + * This function returns a widget in a component created by Glade. + * Call it with the toplevel widget in the component (i.e. a window/dialog), + * or alternatively any widget in the component, and the name of the widget + * you want returned. + */ +GtkWidget* lookup_widget (GtkWidget *widget, + const gchar *widget_name); + + +/* Use this function to set the directory containing installed pixmaps. */ +void add_pixmap_directory (const gchar *directory); + + +/* + * Private Functions. + */ + +/* This is used to create the pixmaps used in the interface. */ +GtkWidget* create_pixmap (GtkWidget *widget, + const gchar *filename); + +/* This is used to create the pixbufs used in the interface. */ +GdkPixbuf* create_pixbuf (const gchar *filename); + +/* This is used to set ATK action descriptions. */ +void glade_set_atk_action_description (AtkAction *action, + const gchar *action_name, + const gchar *description); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/timing.c Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,138 @@ +/* timing.c + * + * get timing info based on file name + * + * file name should have a comment like this: + * R(RMS)-T(in):(out):(end) + * where: + * - RMS is an indication of the RMS value in between in and out + * (to be compared against maximum sample value, 32767 for 16 bit audio) + * - in, out and end are floats, giving the mixing start and end points + * and the length of the file in seconds + */ + +#include "timing.h" +#include "crossfade.h" +#include "debug.h" + +#include <stdio.h> +#include <string.h> +#include <locale.h> + +#if defined(HAVE_ID3LIB) +# include <id3.h> +#endif + +#undef VERBOSE + +int +get_timing_comment(char *filename, quantaudio_t *qa) +/* + * check if the file given has a timing comment + * return 1 if it does and 0 if it doesn't + * store the relevant data in quantaudio + */ +{ + id3_t id3; + int nscanned; + + setlocale(LC_NUMERIC, "C"); + + get_id3(filename, &id3); + if ((nscanned = sscanf(id3.comment, "R:%d-T:%f:%f:%f", &qa->RMS, &qa->mix_in, &qa->mix_out, &qa->length)) < 4) + { + /* tag not right */ +#ifdef VERBOSE + DEBUG(("[crossfade] get_timing_comment: no quantaudio comment\n")); + DEBUG(("[crossfade] get_timing_comment: nscanned=%d (\"%s\")\n", nscanned, id3.comment)); + DEBUG(("[crossfade] get_timing_comment: in %.2f, out %.2f, length %.2f, RMS %d\n", + qa->mix_in, qa->mix_out, qa->length, qa->RMS)); +#endif + return 0; + } + else + { +#ifdef VERBOSE + DEBUG(("[crossfade] get_timing_comment: in %.2f, out %.2f, length %.2f, RMS %d\n", + qa->mix_in, qa->mix_out, qa->length, qa->RMS)); +#endif + return 1; + } +} + +int +get_id3(char *filename, id3_t *id3) +#if defined(HAVE_ID3LIB) +/* get the id3 tag of this file. Return 0 when failed or 1 when ok */ +{ + int status = 0; + ID3Tag *tag; + memset(id3, 0, sizeof(*id3)); + + if ((tag = ID3Tag_New()) != NULL) + { + ID3Frame *frame; + (void) ID3Tag_Link(tag, filename); + + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_COMMENT)) != NULL) + { + ID3Field *field; + if ((field = ID3Frame_GetField(frame, ID3FN_TEXT)) != NULL) + { + (void) ID3Field_GetASCII(field, id3->comment, sizeof(id3->comment)); + DEBUG(("[crossfade] get_id3: comment: %s\n", id3->comment)); + status = 1; + } + } + + if ((frame = ID3Tag_FindFrameWithID(tag, ID3FID_TRACKNUM)) != NULL) + { + ID3Field *field; + if ((field = ID3Frame_GetField(frame, ID3FN_TEXT)) != NULL) + { + char buf[32]; + buf[0] = 0; + (void) ID3Field_GetASCII(field, buf, sizeof(buf)); + id3->track = atoi(buf); + DEBUG(("[crossfade] get_id3: track: %d\n", id3->track)); + status = 1; + } + } + + ID3Tag_Delete(tag); + } + + return status; +} +#else +{ + FILE *fp; + memset(id3, 0, sizeof(*id3)); + + fp = fopen(filename, "r"); /* read only */ + if (fp == NULL) + { + /* file didn't open */ + DEBUG(("[crossfade] get_id3: file %s didn't open !\n", filename)); + return 0; + } + if (fseek(fp, -128, SEEK_END) < 0) + { + /* problem rewinding */ + DEBUG(("[crossfade] get_id3: problem rewinding on %s !\n", filename)); + return 0; + } + else + { + /* we rewound successfully */ + if (fread(id3, 128, 1, fp) < 0) + { + /* read error */ + DEBUG(("[crossfade] get_id3: read error on %s !\n", filename)); + return 0; + } + } + + return 1; +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/crossfade/timing.h Fri Apr 24 05:57:35 2009 -0500 @@ -0,0 +1,20 @@ +/* quantaudio parameters */ +typedef struct +{ + int RMS; + float mix_in; + float mix_out; + float length; +} +quantaudio_t; + +/* id3 stuff is taken from the id3 GPL program */ +typedef struct +{ + char comment[1024]; + int track; +} +id3_t; + +int get_timing_comment(char *filename, quantaudio_t *qa); +int get_id3(char *filename, id3_t *id3);