Mercurial > audlegacy-plugins
diff src/crossfade/configure.c @ 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 | |
children | 440cb96f005f |
line wrap: on
line diff
--- /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); +}