Index: configure.ac
===================================================================
--- configure.ac	(revision 5823)
+++ configure.ac	(working copy)
@@ -1076,6 +1076,7 @@
 plugins/rb/Makefile
 plugins/power-manager/Makefile
 plugins/visualizer/Makefile
+plugins/equalizer/Makefile
 plugins/mmkeys/Makefile
 bindings/Makefile
 bindings/python/Makefile
Index: lib/rb-marshal.list
===================================================================
--- lib/rb-marshal.list	(revision 5823)
+++ lib/rb-marshal.list	(working copy)
@@ -39,3 +39,4 @@
 VOID:ULONG,FLOAT
 VOID:OBJECT,BOOLEAN
 VOID:STRING,STRING,POINTER,POINTER
+VOID:UINT,DOUBLE
Index: plugins/equalizer/rb-equalizer-plugin.c
===================================================================
--- plugins/equalizer/rb-equalizer-plugin.c	(revision 0)
+++ plugins/equalizer/rb-equalizer-plugin.c	(revision 0)
@@ -0,0 +1,1020 @@
+/*
+ * rb-equalizer-plugin.c
+ *
+ * Copyright (C) 2006-2007 - Christophe Dehais <christophe.dehais@gmail.com>
+ *
+ * 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, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h> /* For strlen */
+#include <glib/gi18n-lib.h>
+#include <gmodule.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <glib-object.h>
+#include <glade/glade.h>
+#include <gst/gst.h>
+#include <math.h>
+
+#include "rb-plugin.h"
+#include "rb-debug.h"
+#include "rb-shell.h"
+#include "rb-dialog.h"
+#include "rb-file-helpers.h"
+#include "rb-shell-player.h"
+#include "rb-player.h"
+#include "rb-player-gst-filter.h"
+
+#include "rb-equalizer.h"
+
+static const gchar *config_file = "eq-config.ini";
+static const gchar *presets_file = "eq-presets.ini";
+
+
+#define RB_TYPE_EQUALIZER_PLUGIN        (rb_equalizer_plugin_get_type ())
+#define RB_EQUALIZER_PLUGIN(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_EQUALIZER_PLUGIN, RBEqualizerPlugin))
+#define RB_EQUALIZER_PLUGIN_CLASS(k)        (G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_EQUALIZER_PLUGIN, RBEqualizerPluginClass))
+#define RB_IS_EQUALIZER_PLUGIN(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_EQUALIZER_PLUGIN))
+#define RB_IS_EQUALIZER_PLUGIN_CLASS(k)        (G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_EQUALIZER_PLUGIN))
+#define RB_EQUALIZER_PLUGIN_GET_CLASS(o)    (G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_EQUALIZER_PLUGIN, RBEqualizerPluginClass))
+
+typedef struct _EqFilterInfo EqFilterInfo;
+
+typedef struct
+{
+    RBPlugin parent;
+
+    GladeXML *config_xml;
+    GtkWidget *preferences;
+    GtkWidget *equalizer_win;
+    RBEqualizer *equalizer;
+
+    GtkWindow *main_window;
+
+    GtkActionGroup *action_group;
+    guint ui_merge_id;
+
+    GKeyFile *config_file;
+
+    gboolean enabled;
+    gboolean animated;
+    RBEqualizerBehavior behavior;
+    gdouble *frequencies;
+    gsize nb_frequencies;
+    gchar *current_preset;
+    
+    RBPlayer *player;
+    /* model storing the info for each available filters */
+    GtkListStore *gst_filters;
+
+    gchar *filter_name;
+    EqFilterInfo *filter_info;
+    GstElement *filter;
+    gboolean activated;
+} RBEqualizerPlugin;
+
+
+typedef struct
+{
+    RBPluginClass parent_class;
+} RBEqualizerPluginClass;
+
+
+G_MODULE_EXPORT GType register_rb_plugin (GTypeModule *module);
+GType    rb_equalizer_plugin_get_type        (void) G_GNUC_CONST;
+
+
+
+static void rb_equalizer_plugin_init (RBEqualizerPlugin *plugin);
+static void rb_equalizer_plugin_finalize (GObject *object);
+static void impl_activate (RBPlugin *plugin, RBShell *shell);
+static void impl_deactivate (RBPlugin *plugin, RBShell *shell);
+static gboolean
+impl_is_configurable (RBPlugin *plugin);
+static GtkWidget * impl_create_configure_dialog (RBPlugin *plugin);
+
+static void cmd_show_equalizer (GtkAction *action, RBEqualizerPlugin *plugin);
+
+RB_PLUGIN_REGISTER(RBEqualizerPlugin, rb_equalizer_plugin)
+
+static GtkToggleActionEntry rb_equalizer_plugin_actions [] = {
+	{ "EqualizerDisplayWidget", GTK_STOCK_PREFERENCES, "Show Equalizer ...", NULL,
+	  N_("Show the equalizer"),
+	  G_CALLBACK (cmd_show_equalizer), FALSE
+	}
+};
+
+
+static const gchar *toolbar_ui_path = "/ToolBar/ToolBarPluginPlaceholder";
+
+
+static void
+rb_equalizer_plugin_class_init (RBEqualizerPluginClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    RBPluginClass *plugin_class = RB_PLUGIN_CLASS (klass);
+
+    object_class->finalize = rb_equalizer_plugin_finalize;
+
+    plugin_class->activate = impl_activate;
+    plugin_class->deactivate = impl_deactivate;
+    plugin_class->create_configure_dialog = impl_create_configure_dialog;
+    plugin_class->is_configurable = impl_is_configurable;
+}
+
+static void
+rb_equalizer_plugin_init (RBEqualizerPlugin *plugin)
+{
+    rb_debug ("RBEqualizerPlugin initialising");
+
+    plugin->enabled = FALSE;
+    plugin->animated = FALSE;
+    plugin->behavior = RB_EQ_BEHAVIOR_DIRECT;
+    plugin->current_preset = "Custom";
+}
+
+static void
+rb_equalizer_plugin_finalize (GObject *object)
+{
+    rb_debug ("RBEqualizerPlugin finalising");
+
+    G_OBJECT_CLASS (rb_equalizer_plugin_parent_class)->finalize (object);
+}
+
+
+/*************************************************
+ *
+ * Gstreamer equalizer filter element handling
+ *
+ ************************************************/
+
+struct _EqFilterInfo {
+    gchar *name;
+    GstElementFactory *factory;  /* NULL if filter unavailable */
+    gdouble min_gain;
+    gdouble max_gain;
+    gdouble *frequencies;
+    gint nb_bands;
+
+    GtkWidget *filter_ui;
+    GtkWidget * (*build_filter_ui) (RBEqualizerPlugin *);
+    /* This function takes what the user want
+     * and try to configure the filter as better as it can.
+     * Actual bands setting is stored in the frequencies and nb_bands field of the *info structure.
+     * passing NULL as frequencies sets the filter to its default settings.
+     */
+    void (*configure_bands) (RBEqualizerPlugin *plugin, gdouble *frequencies, gint nb_bands);
+    /* the callback to connect to band_changed signal of the equalizer widget */
+    void (*band_changed_cb) (RBEqualizer *equalizer, gint band_num, gdouble new_value,
+                             RBEqualizerPlugin *plugin);
+};
+
+static void equalizer_configure_bands (RBEqualizerPlugin *plugin, gdouble *frequencies, gint nb_bands);
+static void equalizer_band_changed_cb (RBEqualizer *equalizer, gint band_num, gdouble new_value,
+                                       RBEqualizerPlugin *plugin);
+static GtkWidget * equalizer_build_filter_ui (RBEqualizerPlugin *plugin);
+
+static void ladspa_Eq_configure_bands (RBEqualizerPlugin *plugin, gdouble *frequencies, gint nb_bands);
+static void ladspa_Eq_band_changed_cb (RBEqualizer *equalizer, gint band_num, gdouble new_value,
+                                       RBEqualizerPlugin *plugin);
+static GtkWidget * ladspa_Eq_build_filter_ui (RBEqualizerPlugin *plugin);
+
+static void ladspa_tap_equalizer_configure_bands (RBEqualizerPlugin *plugin, gdouble *frequencies, gint nb_bands);
+static void ladspa_tap_equalizer_band_changed_cb (RBEqualizer *equalizer, gint band_num, gdouble new_value,
+                                                  RBEqualizerPlugin *plugin);
+static GtkWidget * ladspa_tap_equalizer_build_filter_ui (RBEqualizerPlugin *plugin);
+
+
+static EqFilterInfo filter_infos[] = {
+    {"equalizer-nbands", NULL,
+      -24.0, 12.0,
+      NULL, 0,
+      NULL,
+      equalizer_build_filter_ui,
+      equalizer_configure_bands,
+      equalizer_band_changed_cb
+    },
+    {"ladspa-tap-equalizer", NULL,
+      -50.0, 20.0, 
+      NULL, 0,
+      NULL,
+      ladspa_Eq_build_filter_ui,
+      ladspa_tap_equalizer_configure_bands,
+      ladspa_tap_equalizer_band_changed_cb
+    },
+    {"ladspa-Eq", NULL,
+      -48.0, 30.0,
+      NULL, 0,
+      NULL,
+      ladspa_tap_equalizer_build_filter_ui,
+      ladspa_Eq_configure_bands,
+      ladspa_Eq_band_changed_cb
+    }
+};
+
+
+/******* gst-bad equalizer ********/
+
+/* TODO: atm, this ignores the parameters and just sets default values */
+
+#define LOWEST_FREQ (20.0)
+#define HIGHEST_FREQ (20000.0)
+
+static void
+equalizer_configure_bands (RBEqualizerPlugin *plugin,
+			   gdouble *frequencies,
+			   gint nb_bands)
+{
+    EqFilterInfo *info = plugin->filter_info;
+    GstElement *filter = plugin->filter;
+    gint i;
+    gdouble step;
+
+    g_object_get (filter, "num-bands", &info->nb_bands, NULL);
+    rb_debug ("default nb_bands = %d", info->nb_bands);
+    info->frequencies = g_new (gdouble, info->nb_bands);
+
+    /* XXX here I apply the same computation as found in gstiirequalizer.c */
+    step = pow (HIGHEST_FREQ / LOWEST_FREQ, 1.0 / (info->nb_bands - 1));
+    info->frequencies[0] = LOWEST_FREQ;
+    for (i=1; i<info->nb_bands; i++) {
+        info->frequencies[i] = info->frequencies[i-1] * step;
+    }
+
+}
+
+static void
+equalizer_band_changed_cb (RBEqualizer *equalizer,
+			   gint band_num,
+			   gdouble new_value,
+			   RBEqualizerPlugin *plugin)
+{
+    GstObject *band = gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (plugin->filter), band_num);
+    g_assert (band != NULL);
+    g_object_set (band, "gain", (gfloat) new_value, NULL);
+    g_object_unref (G_OBJECT (band));
+}
+
+
+static GtkWidget *
+equalizer_build_filter_ui (RBEqualizerPlugin *plugin)
+{
+    GtkWidget *table;
+    GtkWidget *label;
+    GtkWidget *spin;
+
+    table = gtk_table_new (2, 2, FALSE);
+    gtk_container_set_border_width (GTK_CONTAINER (table), 5);
+
+    /* number of band spin button */
+    label = gtk_label_new ("Number of bands:");
+    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+    gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, GTK_FILL, GTK_SHRINK, 2, 2);
+    spin = gtk_spin_button_new_with_range (2.0, 64, 1.0);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 15.0);
+    gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 0);
+    gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 0, 1, GTK_EXPAND, GTK_SHRINK, 2, 2);
+    /*g_signal_connect (G_OBJECT (spin), "value-changed",
+                      G_CALLBACK (on_nb_bands_spin_changed), plugin);*/
+
+    /* band width spin button */
+    label = gtk_label_new ("Bands width:");
+    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+    gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, GTK_FILL, GTK_SHRINK, 2, 2);
+    spin = gtk_spin_button_new_with_range (0.1, 5.0, 0.1);
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 1.0);
+    gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spin), 1);
+    gtk_table_attach (GTK_TABLE (table), spin, 1, 2, 1, 2, GTK_EXPAND, GTK_SHRINK, 2, 2);
+    /* g_signal_connect (G_OBJECT (spin), "value-changed",
+                         G_CALLBACK (on_bands_width_spin_changed), plugin);*/
+    
+    return table;
+}
+
+
+/******* ladspa-Eq ********/
+
+static gdouble ladspa_Eq_frequencies[] =
+             {31.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0};
+static const gchar * ladspa_Eq_prop_names[] =
+             { "param-31-Hz", "param-63-Hz", "param-125-Hz", "param-250-Hz", "param-500-Hz",
+               "param-1-kHz", "param-2-kHz", "param-4-kHz", "param-8-kHz", "param-16-kHz" };
+
+
+/* ladspa-Eq is not configurable, so the parameters are just ignored */
+static void
+ladspa_Eq_configure_bands (RBEqualizerPlugin *plugin,
+			   gdouble *frequencies,
+			   gint nb_bands)
+{
+    EqFilterInfo *info = plugin->filter_info;
+
+    rb_debug ("Configuring ladspa-Eq bands");
+    info->frequencies = ladspa_Eq_frequencies;
+    info->nb_bands = 10;
+    return;
+}
+
+
+static void
+ladspa_Eq_band_changed_cb (RBEqualizer *equalizer,
+			   gint band_num,
+			   gdouble new_value,
+			   RBEqualizerPlugin *plugin)
+{
+    const gchar *prop_name = ladspa_Eq_prop_names [band_num];
+    gfloat gain = (gfloat) new_value;
+
+    g_object_set (G_OBJECT (plugin->filter), prop_name, gain, NULL);
+}
+
+
+static GtkWidget *
+ladspa_Eq_build_filter_ui (RBEqualizerPlugin *plugin)
+{
+    GtkWidget *vbox;
+    GtkWidget *check;
+    GString *freq_label;
+    gint i;
+    gdouble frequency;
+
+    vbox = gtk_vbox_new (FALSE, 2);
+
+    freq_label = g_string_new ("");
+
+    for (i=0; i<G_N_ELEMENTS(ladspa_Eq_frequencies); i++) {
+        frequency = ladspa_Eq_frequencies[i];
+
+        if (frequency < 1000) {
+            g_string_printf (freq_label, "%d", (gint)frequency);
+        } else {
+            gdouble kilohz = frequency / 1000.0;
+            /* round to 0 or 1 digit */
+            kilohz = floor (kilohz * 10.0 + 0.5) / 10.0;
+            if (floor (kilohz) == kilohz) {
+                kilohz = floor (kilohz + 0.5);
+                g_string_printf (freq_label, "%dk", (gint)kilohz);
+            } else {
+                g_string_printf (freq_label, "%0.1fk", kilohz);
+            }
+        }
+    
+        check = gtk_check_button_new_with_label (freq_label->str);
+        gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 2);
+    }
+    
+    g_string_free (freq_label, TRUE);
+
+    return vbox;
+}
+
+
+/******* ladspa-tap-equalizer ********/
+
+static const gchar * ladspa_tap_equalizer_freq_prop_names[] =
+             { "Band-1-Freq--Hz-", "Band-2-Freq--Hz-", "Band-3-Freq--Hz-", "Band-4-Freq--Hz-",
+               "Band-5-Freq--Hz-", "Band-6-Freq--Hz-", "Band-7-Freq--Hz-", "Band-8-Freq--Hz-"};
+static const gchar * ladspa_tap_equalizer_gain_prop_names[] =
+             { "Band-1-Gain--dB-", "Band-2-Gain--dB-", "Band-3-Gain--dB-", "Band-4-Gain--dB-",
+               "Band-5-Gain--dB-", "Band-6-Gain--dB-", "Band-7-Gain--dB-", "Band-8-Gain--dB-"};
+
+
+static void
+ladspa_tap_equalizer_configure_bands (RBEqualizerPlugin *plugin,
+				      gdouble *frequencies,
+				      gint nb_bands)
+{
+    EqFilterInfo *info = plugin->filter_info;
+    GstElement *filter = plugin->filter;
+
+    if (frequencies == NULL) {
+        gint i;
+
+        if (info->frequencies != NULL)
+            g_free (info->frequencies);
+
+        info->nb_bands = 8;
+        info->frequencies = g_new (gdouble, info->nb_bands);
+
+        /* retrieve gst filter default values */
+        for (i=0; i<info->nb_bands; i++) {
+            gfloat freq;
+            const gchar *prop_name = ladspa_tap_equalizer_freq_prop_names[i];
+            rb_debug ("prop: %s", prop_name);
+            g_object_get (G_OBJECT (filter), prop_name, &freq, NULL);
+            info->frequencies[i] = (gdouble)freq;
+        }
+    } else {
+
+        /* TODO */
+
+    }
+
+}
+
+
+static void
+ladspa_tap_equalizer_band_changed_cb (RBEqualizer *equalizer,
+				      gint band_num,
+				      gdouble new_value,
+				      RBEqualizerPlugin *plugin)
+{
+    gfloat gain = (gfloat) new_value;
+    const gchar *prop_name = ladspa_tap_equalizer_gain_prop_names[band_num];
+
+    rb_debug ("Prop: %s", prop_name);
+    g_object_set (G_OBJECT (plugin->filter), prop_name, gain, NULL);
+}
+
+
+static GtkWidget *
+ladspa_tap_equalizer_build_filter_ui (RBEqualizerPlugin *plugin)
+{
+    GtkWidget *vbox;
+    GtkWidget *check;
+    gint i;
+
+    vbox = gtk_vbox_new (FALSE, 2);
+
+
+    for (i=0; i<G_N_ELEMENTS(ladspa_Eq_frequencies); i++) {
+        check = gtk_check_button_new_with_label ("duh");
+        gtk_box_pack_start (GTK_BOX (vbox), check, FALSE, FALSE, 2);
+    }
+    
+    return vbox;
+}
+
+
+/* Also fills the 'factory' field of filter_infos */
+static GList *
+get_available_filters ()
+{
+    static GList *available_filters = NULL;
+    gint i;
+
+    if (available_filters == NULL) {
+        for (i=0; i<G_N_ELEMENTS(filter_infos); i++) {
+            filter_infos[i].factory  = gst_element_factory_find (filter_infos[i].name);
+            if (filter_infos[i].factory != NULL) {
+                available_filters = g_list_prepend (available_filters, &filter_infos[i]);
+            }
+            rb_debug ("Equalizer filter %s %s", filter_infos[i].name,
+                       filter_infos[i].factory ? "present" : "not present" );
+        }
+    }
+
+    return available_filters;
+}
+
+
+static GstElement *
+make_equalizer_element (RBEqualizerPlugin *plugin)
+{
+    gchar *name;
+    EqFilterInfo *info;
+    GList *filters, *filter;
+
+    name = plugin->filter_name;
+    if (!plugin->filter_name)
+    {
+        g_warning ("equalizer plugin has no filter name, using equalizer-nbands\n");
+        name = "equalizer-nbands";
+    }
+
+    rb_debug ("Trying to make filter element: %s", name);
+
+    filters = get_available_filters ();
+    if (filters == NULL) {
+        plugin->filter_info = NULL;
+        return NULL;
+    }
+
+    for (filter=filters; filter != NULL; filter=filter->next) {
+        info = (EqFilterInfo *)filter->data;
+        if (strcmp (info->name, name) == 0) break;
+    }
+
+    if (filter == NULL) { /* fallback to first available filter */
+        info = (EqFilterInfo *)filters->data;
+    } else {
+        info = (EqFilterInfo *)filter->data;
+    }
+    plugin->filter_info = info;
+    return gst_element_factory_create(info->factory, "equalizer");
+}
+
+
+
+
+/*************************************
+ *
+ * Plugin activation
+ *
+ *************************************/
+
+static gboolean
+on_equalizer_win_delete (GtkWidget *win, GdkEvent *event, RBEqualizerPlugin *plugin)
+{
+    GtkAction *action;
+    rb_equalizer_save_presets (plugin->equalizer);
+
+    action = gtk_action_group_get_action (plugin->action_group, "EqualizerDisplayWidget");
+    gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+
+    rb_equalizer_save_presets (plugin->equalizer);
+
+    return TRUE;
+}
+
+
+static void
+cmd_show_equalizer (GtkAction          *action,
+		    RBEqualizerPlugin  *plugin)
+{
+    if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
+	if (plugin->equalizer_win == NULL) {
+	    GtkWidget *win;
+
+	    win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	    gtk_window_set_title (GTK_WINDOW (win), "Equalizer");
+	    gtk_container_set_border_width (GTK_CONTAINER (win), 2);
+
+	    g_signal_connect (G_OBJECT (win), "delete-event",
+			  G_CALLBACK (on_equalizer_win_delete), plugin);
+
+	    gtk_container_add (GTK_CONTAINER (win), GTK_WIDGET (plugin->equalizer));
+
+	    gtk_window_set_transient_for (GTK_WINDOW (win), plugin->main_window);
+	    gtk_widget_show_all (win);
+
+	    plugin->equalizer_win = win;
+	    rb_debug ("creating widget");
+	} else {
+	    gtk_window_present (GTK_WINDOW (plugin->equalizer_win));
+	}
+    } else if (plugin->equalizer_win != NULL) {
+	gtk_widget_hide (plugin->equalizer_win);
+    }
+}
+
+
+
+static void
+print_config (RBEqualizerPlugin *plugin)
+{
+    gint i;
+    rb_debug ("Plugin Config:");
+    rb_debug ("\tenabled = %s", plugin->enabled ? "true" : "false");
+    rb_debug ("\tanimated = %s", plugin->animated ? "true" : "false");
+    rb_debug ("\tbehavior = %s", (plugin->behavior == RB_EQ_BEHAVIOR_SMOOTH) ? "smooth" : "direct");
+    rb_debug ("\tpreset = %s", plugin->current_preset);
+    rb_debug ("\tfilter = %s", plugin->filter_name);
+    rb_debug ("\tfrequencies = ");
+    for (i=0; i<plugin->nb_frequencies; i++) {
+        rb_debug ("%.1f;", plugin->frequencies[i]);
+    }
+}
+
+
+static void
+load_config (RBEqualizerPlugin *plugin, const gchar *cfgfile)
+{
+    GKeyFile * cfg_keyfile;
+    gboolean read_ok;
+    gchar *behavior_str;
+    gchar *cfgfile_path;
+
+    cfg_keyfile = g_key_file_new ();
+    g_key_file_set_list_separator (cfg_keyfile, ';');
+    plugin->config_file = cfg_keyfile;
+
+    cfgfile_path = rb_plugin_find_file (RB_PLUGIN (plugin), cfgfile);
+    if (!cfgfile_path)
+    {
+        rb_debug ("could not get cfgfile_path\n");
+        return;
+    }
+
+    rb_debug ("cfgfile_path = \"%s\"", cfgfile_path);
+
+    read_ok = g_key_file_load_from_file (cfg_keyfile,
+                                         cfgfile_path,
+                                         G_KEY_FILE_NONE , /*G_KEY_FILE_KEEP_COMMENTS, */
+                                         NULL);
+
+    if (!read_ok) {
+        rb_debug ("could not load equalizer config file\n");
+        return;
+    }
+
+    plugin->enabled = g_key_file_get_boolean (cfg_keyfile, "UI", "enabled", NULL);
+    plugin->animated = g_key_file_get_boolean (cfg_keyfile, "UI", "animated", NULL);
+    behavior_str = g_key_file_get_string (cfg_keyfile, "UI", "behavior", NULL);
+    if (behavior_str != NULL) {
+        if (strcmp (behavior_str, "smooth") == 0) {
+            plugin->behavior = RB_EQ_BEHAVIOR_SMOOTH;
+        } else {
+            plugin->behavior = RB_EQ_BEHAVIOR_DIRECT;
+        }
+        g_free (behavior_str);
+    }
+    plugin->current_preset = g_key_file_get_string (cfg_keyfile, "UI", "current_preset", NULL);
+
+    plugin->filter_name = g_key_file_get_string (cfg_keyfile, "Bands", "filter", NULL);
+    plugin->frequencies = g_key_file_get_double_list (cfg_keyfile, "Bands", "frequencies",
+                                                      &(plugin->nb_frequencies), NULL);
+
+    print_config (plugin);
+}
+
+
+static void
+save_config (RBEqualizerPlugin *plugin)
+{
+    FILE *file;
+    gchar *data;
+
+    if (plugin->config_file == NULL)
+        return;
+
+    file = fopen (rb_plugin_find_file (RB_PLUGIN (plugin), config_file), "w");
+    if (file == NULL) {
+        rb_debug ("couldn't open config file");
+	return;
+    }
+    data = g_key_file_to_data (plugin->config_file, NULL, NULL);
+    fputs (data, file);
+    fclose (file);
+    g_free (data);
+}
+
+/*
+static void save_config (RBEqualizerPlugin *plugin) {
+    if (plugin->config_file == NULL)
+        return;
+
+
+    g_key_file_set_boolean (plugin->config_file, "UI", "enabled", plugin->enabled);
+    g_key_file_set_boolean (plugin->config_file, "UI", "animated", plugin->animated);
+    if (plugin->behavior == RB_EQ_BEHAVIOR_SMOOTH) {
+        g_key_file_set_string (plugin->config_file, "UI", "behavior", "smooth");
+    } else {
+        g_key_file_set_string (plugin->config_file, "UI", "behavior", "direct");
+    }
+    g_key_file_set_string (plugin->config_file, "UI", "current_preset", plugin->current_preset);
+
+
+    g_key_file_set_string (plugin->config_file, "Bands", "filter", plugin->filter_name);
+    g_key_file_set_double_list (plugin->config_file, "Bands", "frequencies",
+                                plugin->frequencies, plugin->nb_frequencies);
+
+}
+*/
+
+
+static void
+on_equalizer_preset_changed (RBEqualizer *equalizer,
+			     gchar *name,
+			     RBEqualizerPlugin *plugin)
+{
+    rb_debug ("Preset changed to [%s]", name);
+    plugin->current_preset = name;
+    g_key_file_set_string (plugin->config_file, "UI", "current_preset", plugin->current_preset);
+    save_config (plugin);
+}
+
+static void
+on_equalizer_enabled_toggled (RBEqualizer *equalizer,
+			      gboolean enabled,
+			      RBEqualizerPlugin *plugin)
+{
+    if ((plugin->player == NULL) || (plugin->filter == NULL))
+        return;
+
+    plugin->enabled = enabled;
+
+    if (enabled) {
+        rb_player_gst_filter_add_filter (RB_PLAYER_GST_FILTER (plugin->player), plugin->filter);
+    } else {
+        gst_object_ref (plugin->filter);
+        rb_player_gst_filter_remove_filter (RB_PLAYER_GST_FILTER (plugin->player), plugin->filter);
+    }
+
+    g_key_file_set_boolean (plugin->config_file, "UI", "enabled", plugin->enabled);
+    save_config (plugin);
+}
+
+
+static gboolean
+impl_is_configurable (RBPlugin *rbplugin)
+{
+	RBEqualizerPlugin *plugin = RB_EQUALIZER_PLUGIN (rbplugin);
+	return plugin->activated;
+}
+
+static void
+impl_activate (RBPlugin *rbplugin,
+               RBShell *shell)
+{
+    RBEqualizerPlugin *plugin = RB_EQUALIZER_PLUGIN (rbplugin);
+    GtkUIManager *uimanager;
+    GtkWidget *equalizer;
+    RBPlayer *player;
+
+    /* load settings from config file */
+    load_config (plugin, config_file);
+
+    /* check the player backend supports the filter interface */
+    g_object_get (G_OBJECT (rb_shell_get_player (shell)), "player", &player, NULL);
+    if (RB_IS_PLAYER_GST_FILTER (player) == FALSE) {
+	    g_warning ("player backend doesn't support the filter interface");
+	    return;
+    }
+
+    /* create filter element */
+    plugin->filter = make_equalizer_element (plugin);
+
+    if (plugin->filter == NULL) {
+        rb_error_dialog (NULL, _("Equalizer Plugin"), "Unable to find an equalizer filter. To function the equalizer plugin needs one of these gstreamer elements: equalizer, ladspa-Eq or ladspa-tap-equalizer");
+        return;
+    }
+
+    /* setup the equalizer filter */
+    rb_debug ("Actual filter: %s", plugin->filter_info->name);
+    plugin->filter_info->configure_bands (plugin, NULL, 0);
+
+    /* create equalizer widget */
+    equalizer = rb_equalizer_new (plugin->filter_info->nb_bands, plugin->filter_info->frequencies);
+
+    rb_equalizer_set_default_behavior (RB_EQUALIZER (equalizer), plugin->behavior);
+    rb_equalizer_set_animated (RB_EQUALIZER (equalizer), plugin->animated);
+    rb_equalizer_set_enabled (RB_EQUALIZER (equalizer), plugin->enabled);
+    rb_equalizer_set_gain_range (RB_EQUALIZER (equalizer),
+                                 plugin->filter_info->min_gain, plugin->filter_info->max_gain);
+    plugin->equalizer = RB_EQUALIZER (equalizer);
+
+    rb_equalizer_set_presets_file (RB_EQUALIZER (equalizer),
+                                   rb_plugin_find_file (RB_PLUGIN (plugin), presets_file));
+    rb_equalizer_set_active_preset (RB_EQUALIZER (equalizer), plugin->current_preset);
+
+    /* bind UI to filter and config */
+    g_signal_connect (G_OBJECT (plugin->equalizer), "band-changed",
+                      G_CALLBACK (plugin->filter_info->band_changed_cb), plugin);
+    g_signal_connect (G_OBJECT (plugin->equalizer), "preset-changed",
+                      G_CALLBACK (on_equalizer_preset_changed), plugin);
+    g_signal_connect (G_OBJECT (plugin->equalizer), "enabled_toggled",
+                      G_CALLBACK (on_equalizer_enabled_toggled), plugin);
+
+    /* insert into pipeline */
+    g_object_get (G_OBJECT (rb_shell_get_player (shell)), "player", &plugin->player, NULL);
+    rb_player_gst_filter_add_filter (RB_PLAYER_GST_FILTER (player), plugin->filter);
+
+
+    /* add a toolbar item for showing the equalizer */
+    uimanager = GTK_UI_MANAGER (rb_shell_get_ui_manager (shell));
+
+    plugin->action_group = gtk_action_group_new ("EqualizerActions");
+    gtk_action_group_add_toggle_actions (plugin->action_group,
+	    				 rb_equalizer_plugin_actions,
+					 G_N_ELEMENTS (rb_equalizer_plugin_actions),
+					 plugin);
+
+    gtk_ui_manager_insert_action_group (uimanager, plugin->action_group, 0);
+
+    plugin->ui_merge_id =  gtk_ui_manager_new_merge_id (uimanager);
+
+    gtk_ui_manager_add_ui (uimanager,
+                           plugin->ui_merge_id,
+                           toolbar_ui_path,
+                           "EqualizerDisplayWidgetItem",
+                           "EqualizerDisplayWidget",
+                           GTK_UI_MANAGER_AUTO,
+                           FALSE);
+
+    g_object_get (shell, "window", &plugin->main_window, NULL);
+    plugin->activated = TRUE;
+}
+
+
+static void
+impl_deactivate (RBPlugin *rbplugin,
+                 RBShell  *shell)
+{
+	RBEqualizerPlugin *plugin = RB_EQUALIZER_PLUGIN (rbplugin);
+	plugin->activated = FALSE;
+}
+
+
+/*************************
+ *
+ * Preferences dialog
+ *
+ ************************/
+
+static void
+preferences_response_cb (GtkWidget *dialog, gint response, RBEqualizerPlugin *plugin)
+{
+    gtk_widget_hide (dialog);
+    /* save config file */
+    save_config (plugin);
+}
+
+
+static void
+on_behavior_radio_toggled (GtkToggleButton *behavior_toggle,
+			   RBEqualizerPlugin *plugin)
+{
+    gboolean active = gtk_toggle_button_get_active (behavior_toggle);
+
+    plugin->behavior = active ? RB_EQ_BEHAVIOR_SMOOTH : RB_EQ_BEHAVIOR_DIRECT;
+
+    /* update config file */
+    if (plugin->behavior == RB_EQ_BEHAVIOR_SMOOTH) {
+        g_key_file_set_string (plugin->config_file, "UI", "behavior", "smooth");
+    } else {
+        g_key_file_set_string (plugin->config_file, "UI", "behavior", "direct");
+    }
+
+    if (plugin->equalizer != NULL) {
+        rb_equalizer_set_default_behavior (plugin->equalizer, plugin->behavior);
+    }
+}
+
+
+static void
+on_animation_check_toggled (GtkToggleButton *anim_toggle,
+			    RBEqualizerPlugin *plugin)
+{
+    plugin->animated = gtk_toggle_button_get_active (anim_toggle);
+
+    /* update config file */
+    g_key_file_set_boolean (plugin->config_file, "UI", "animated", plugin->animated);
+
+    if (plugin->equalizer != NULL) {
+        rb_equalizer_set_animated (plugin->equalizer, plugin->animated);
+    }
+}
+
+
+enum {
+    FILTER_NAME_COLUMN, /* gstreamer filter name (string) */
+    FILTER_INFO_COLUMN, /* filter info (pointer to a EqFilterInfo structure) */
+    N_COLUMNS
+};
+
+
+static void
+on_filter_combo_changed (GtkComboBox *combo,
+                         RBEqualizerPlugin *plugin)
+{
+    GtkWidget *ui_container;
+    GtkWidget *filter_ui;
+    GtkTreeIter iter;
+    EqFilterInfo *info;
+    gchar *name;
+
+    ui_container = glade_xml_get_widget (plugin->config_xml, "filter_ui_container");
+
+    /* remove the UI block corresponding to the current filter element */
+    filter_ui = gtk_bin_get_child (GTK_BIN (ui_container)); /* it's a GtkScrolledWindow */
+    if (filter_ui != NULL) {
+        gtk_container_remove (GTK_CONTAINER (ui_container), filter_ui);
+    }
+
+    /* add the new UI block */
+    gtk_combo_box_get_active_iter (combo, &iter);
+    gtk_tree_model_get (GTK_TREE_MODEL (plugin->gst_filters), &iter,
+                        FILTER_INFO_COLUMN, &info,
+                        FILTER_NAME_COLUMN, &name, -1);
+    if (info->filter_ui == NULL) {
+        info->filter_ui = info->build_filter_ui (plugin);
+        gtk_widget_show_all (info->filter_ui);
+        g_object_ref (info->filter_ui);
+    }
+    gtk_container_add (GTK_CONTAINER (ui_container), info->filter_ui);
+
+    /* save config file */
+    plugin->filter_name = name;
+    g_key_file_set_string (plugin->config_file, "Bands", "filter", plugin->filter_name);
+    save_config (plugin);
+
+}
+
+
+static GtkWidget *
+make_config_widget (RBEqualizerPlugin *plugin)
+{
+    char *gladefile;
+    GtkWidget *behavior_radio;
+    GtkWidget *animation_check;
+    GtkWidget *combo_container;
+    GtkWidget *combo;
+    GtkCellRenderer *renderer;
+    GList *filters;
+
+    gladefile = rb_plugin_find_file (RB_PLUGIN (plugin), "equalizer-prefs.glade");
+    if (gladefile == NULL) {
+        gladefile = g_strdup (rb_file ("equalizer-prefs.glade"));
+        if (gladefile == NULL) {
+            return NULL;
+        }
+    }
+
+    plugin->config_xml = glade_xml_new (gladefile, "equalizer_prefs_notebook", NULL);
+    g_free (gladefile);
+
+    combo_container = glade_xml_get_widget (plugin->config_xml, "filter_combo_alignement");
+    
+    plugin->gst_filters = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER);
+
+    combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (plugin->gst_filters));
+    renderer = gtk_cell_renderer_text_new ();
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,    
+                                    "text", FILTER_NAME_COLUMN, NULL);
+    g_signal_connect (G_OBJECT (combo), "changed",
+                      G_CALLBACK (on_filter_combo_changed), plugin);
+
+    gtk_container_add (GTK_CONTAINER (combo_container), combo);
+
+    filters = get_available_filters();
+    for ( ; filters != NULL; filters = filters->next) {
+        GtkTreeIter iter;
+        EqFilterInfo *info = (EqFilterInfo *)filters->data;
+        rb_debug ("filter name: %s", info->name);
+
+        /* put in combo box */
+        gtk_list_store_append (plugin->gst_filters, &iter);
+        gtk_list_store_set (plugin->gst_filters, &iter,
+                            FILTER_NAME_COLUMN, info->name,
+                            FILTER_INFO_COLUMN, (gpointer)info, -1);
+
+        /* make the current choice active */
+        if (g_str_equal (info->name, plugin->filter_name)) {
+            gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
+        }
+    }
+
+    behavior_radio = glade_xml_get_widget (plugin->config_xml, "smooth_update_radio");
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (behavior_radio),
+                                  (plugin->behavior == RB_EQ_BEHAVIOR_SMOOTH));
+    g_signal_connect (G_OBJECT (behavior_radio), "toggled",
+                      G_CALLBACK (on_behavior_radio_toggled), plugin);
+
+    animation_check = glade_xml_get_widget (plugin->config_xml, "enable_animation_check");
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (animation_check), plugin->animated);
+    g_signal_connect (G_OBJECT (animation_check), "toggled",
+                      G_CALLBACK (on_animation_check_toggled), plugin);
+
+    return glade_xml_get_widget (plugin->config_xml, "equalizer_prefs_notebook");
+}
+
+
+static GtkWidget *
+impl_create_configure_dialog (RBPlugin *rbplugin)
+{
+    RBEqualizerPlugin *plugin = RB_EQUALIZER_PLUGIN (rbplugin);
+
+    if (plugin->preferences == NULL) {
+        GtkWidget *widget;
+
+        widget = make_config_widget (plugin);
+
+        plugin->preferences = gtk_dialog_new_with_buttons ("Equalizer Preferences",
+                                              NULL,
+                                              GTK_DIALOG_DESTROY_WITH_PARENT,
+                                              GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                                              NULL);
+        g_signal_connect (G_OBJECT (plugin->preferences), "response",
+                          G_CALLBACK (preferences_response_cb), plugin);
+        g_signal_connect_swapped (G_OBJECT (plugin->preferences), "delete-event",
+                                  G_CALLBACK (gtk_widget_hide), plugin->preferences);
+
+        gtk_widget_hide_on_delete (plugin->preferences);
+
+        gtk_container_add (GTK_CONTAINER (GTK_DIALOG (plugin->preferences)->vbox), widget);
+    } else {
+       /* update_config_widget (plugin); */
+    }
+
+    gtk_widget_show_all (plugin->preferences);
+    return plugin->preferences;
+}
Index: plugins/equalizer/rb-equalizer.c
===================================================================
--- plugins/equalizer/rb-equalizer.c	(revision 0)
+++ plugins/equalizer/rb-equalizer.c	(revision 0)
@@ -0,0 +1,1382 @@
+/*
+ * RBEqualizer - Composite widget for controlling an audio equalizer
+ *
+ * Copyright (C) 2006-2007 - Christophe Dehais <christophe.dehais@gmail.com>
+ *
+ * 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, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ */
+
+
+#include "rb-equalizer.h"
+#include "rb-marshal.h"
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include "rb-debug.h"
+
+#define CUSTOM_PRESET_NAME   "Custom"
+
+#define RB_EQUALIZER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_EQUALIZER, RBEqualizerPrivate))
+
+
+static void rb_equalizer_init (RBEqualizer *equalizer);
+static void rb_equalizer_class_init (RBEqualizerClass *g_class);
+
+/* signals */
+enum {
+    ENABLED_TOGGLED,
+    BAND_CHANGED,
+    PRESET_CHANGED,
+    LAST_SIGNAL
+};
+
+typedef struct {
+    gint nb_bands;
+    gdouble *frequencies;
+    GtkWidget **scales;
+} RBEqualizerBoard;
+
+/* scale board sub-API */
+static RBEqualizerBoard * board_create (RBEqualizer *equalizer,
+                                        gint nb_bands, gdouble *frequencies);
+static void board_set_values (RBEqualizer *equalizer, RBEqualizerBandsSetting *setting);
+static void board_smooth_values (RBEqualizer *equalizer, gint modified_scale);
+static RBEqualizerBandsSetting* board_adapt_setting (RBEqualizerBoard *board, RBEqualizerBandsSetting *setting);
+static void board_fill_preset (RBEqualizerBoard *board, RBEqualizerBandsSetting *preset);
+
+/* Presets are stored in a GtkTreeModel */
+enum {
+    NAME_COLUMN,            /* identifier name (string) */
+    LOCALIZED_NAME_COLUMN,  /* localized name  (string)*/
+    BANDS_SETTING_COLUMN,   /* bands gain info (pointer to a RBEqualizerBandsSetting struct) */
+    N_COLUMNS
+};
+
+/* private data */
+struct _RBEqualizerPrivate {
+    GtkWidget *preset_combo;
+
+    GtkListStore *presets;
+
+    gchar *presets_filename;
+    GKeyFile *presets_file;
+
+    RBEqualizerBoard *board;
+    gdouble min_gain, max_gain;
+
+    GtkWidget *save_dialog;
+    GtkWidget *preset_name_combo;
+
+    GtkTreeIter current_preset;
+    GtkTreeIter custom_preset; /* points to the special 'Custom' preset */
+
+    /* other UI elements, mainly here to handle sensistivity */
+    GtkWidget *save_button;
+    GtkWidget *delete_button;
+    GtkWidget *enabled_button;
+    GtkWidget *board_box;
+    /* ruler */
+    GtkWidget *ruler;
+    GtkWidget *ruler_label_min;
+    GtkWidget *ruler_label_zero;
+    GtkWidget *ruler_label_max;
+
+    /* smoothing */
+    RBEqualizerBehavior default_behavior;
+    RBEqualizerBehavior current_behavior;
+    gboolean block_smoothing;
+
+    /* animation */
+    gboolean animated;
+    gdouble *gain_end_values;
+    gdouble *gain_steps;
+    guint anim_timeout;
+
+    gboolean enabled;
+};
+
+
+GType rb_equalizer_get_type (void)
+{
+    static GType equalizer_type = 0;
+  
+    if (!equalizer_type) {
+        static const GTypeInfo equalizer_info = {
+            sizeof (RBEqualizerClass),
+            NULL,               /* base_init */
+            NULL,               /* base_finalize */
+            (GClassInitFunc) rb_equalizer_class_init,
+            NULL,               /* class_finalize */
+            NULL,               /* class_data */
+            sizeof (RBEqualizer),
+            0,                  /* n_preallocs */
+            (GInstanceInitFunc) rb_equalizer_init
+        };
+
+        equalizer_type = g_type_register_static (GTK_TYPE_VBOX,
+                                              "RBEqualizer",
+                                              &equalizer_info,
+                                              0);
+    }
+
+    return equalizer_type;
+}
+
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+rb_equalizer_class_init (RBEqualizerClass *g_class)
+{
+    GType param_types[2];
+
+    g_type_class_add_private (g_class, sizeof (RBEqualizerPrivate));
+
+    /* signals */
+    param_types[0] =
+    signals[BAND_CHANGED] = g_signal_new ("band-changed",
+                                           G_TYPE_FROM_CLASS (g_class),
+                                           G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+                                           0, /* G_STRUCT_OFFSET (RBEqualizerClass, bands_changed) */ /* class closure */
+                                           NULL /* accumulator */,
+                                           NULL /* accu_data */,
+                                           rb_marshal_VOID__UINT_DOUBLE,
+                                           G_TYPE_NONE /* return_type */,
+                                           2           /* n_params */,
+                                           G_TYPE_INT  /* param_types */,
+                                           G_TYPE_DOUBLE,
+                                           NULL);
+
+    signals[ENABLED_TOGGLED] = g_signal_new ("enabled-toggled",
+                                           G_TYPE_FROM_CLASS (g_class),
+                                           G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+                                           0, /*G_STRUCT_OFFSET (RBEqualizerClass, enabled_toggled)*/ /* class closure */
+                                           NULL /* accumulator */,
+                                           NULL /* accu_data */,
+                                           g_cclosure_marshal_VOID__BOOLEAN,
+                                           G_TYPE_NONE /* return_type */,
+                                           1           /* n_params */,
+                                           G_TYPE_BOOLEAN /* param_types */,
+                                           NULL);
+
+    signals[PRESET_CHANGED] = g_signal_new ("preset_changed",
+                                           G_TYPE_FROM_CLASS (g_class),
+                                           G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
+                                           0, /*G_STRUCT_OFFSET (RBEqualizerClass, enabled_toggled)*/ /* class closure */
+                                           NULL /* accumulator */,
+                                           NULL /* accu_data */,
+                                           g_cclosure_marshal_VOID__STRING,
+                                           G_TYPE_NONE /* return_type */,
+                                           1           /* n_params */,
+                                           G_TYPE_STRING /* param_types */,
+                                           NULL);
+
+}
+
+
+/*******************************
+ *
+ * Presets combo box
+ *
+ ******************************/
+
+struct search_preset_data {
+    const gchar *name;
+    gint column;
+    GtkTreeIter *iter;
+};
+
+static gboolean compare_presets (GtkTreeModel *model,
+                                 GtkTreePath *path, GtkTreeIter *iter,
+                                 struct search_preset_data *data)
+{
+    char *preset_name;
+    gboolean found = FALSE;
+
+    gtk_tree_model_get (model, iter, data->column, &preset_name, -1);
+
+    if (g_str_equal (data->name, preset_name)) {
+        data->iter = gtk_tree_iter_copy(iter);
+        found = TRUE;
+    }
+
+    g_free (preset_name);
+    return found;
+}
+
+
+/* returned iter must be freed after use */
+static GtkTreeIter *
+search_preset (RBEqualizer *equalizer, const gchar *name, gboolean localized)
+{
+    struct search_preset_data data;
+
+    data.column = localized ? LOCALIZED_NAME_COLUMN : NAME_COLUMN;
+    data.name = name;
+    data.iter = NULL;
+
+   /* rb_debug ("searching preset %s", name); */
+
+    gtk_tree_model_foreach (GTK_TREE_MODEL (equalizer->priv->presets),
+                            (GtkTreeModelForeachFunc) compare_presets, &data);
+   /* rb_debug ("done (%p)", data.iter);   */
+
+    return data.iter;
+}
+
+
+void
+rb_equalizer_set_active_preset (RBEqualizer *equalizer, const gchar *name)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkTreeIter *iter;
+
+    iter = search_preset (equalizer, name, FALSE);
+    if (iter != NULL) {
+        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), iter);
+        priv->current_preset = *iter;
+        gtk_tree_iter_free (iter);
+    }
+}
+
+
+void
+rb_equalizer_set_active_preset_localized (RBEqualizer *equalizer, const gchar *name)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkTreeIter *iter;
+
+    iter = search_preset (equalizer, name, TRUE);
+    if (iter != NULL) {
+        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), iter);
+        priv->current_preset = *iter;
+        gtk_tree_iter_free (iter);
+    }
+}
+
+
+RBEqualizerBandsSetting *
+rb_equalizer_get_current_setting (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBandsSetting *setting;
+    RBEqualizerBandsSetting *adapted_setting;
+
+    gtk_tree_model_get (GTK_TREE_MODEL (priv->presets), &priv->current_preset,
+                        BANDS_SETTING_COLUMN, &setting, -1);
+
+    adapted_setting = board_adapt_setting (priv->board,  setting);
+
+    return adapted_setting;
+}
+
+
+void
+rb_equalizer_set_enabled (RBEqualizer *equalizer, gboolean enabled)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    gchar *name;
+
+    gtk_widget_set_sensitive (priv->preset_combo, enabled);
+    gtk_widget_set_sensitive (priv->save_button, enabled);
+
+    gtk_tree_model_get (GTK_TREE_MODEL (priv->presets), &priv->current_preset,
+                        NAME_COLUMN, &name, -1);
+    gtk_widget_set_sensitive (priv->delete_button,
+                              enabled && !g_str_equal (name, CUSTOM_PRESET_NAME));
+
+    gtk_widget_set_sensitive (priv->board_box, enabled);
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_button), enabled);
+
+    priv->enabled = enabled;
+    g_signal_emit (equalizer, signals[ENABLED_TOGGLED], 0, priv->enabled, NULL);
+}
+
+
+gboolean
+rb_equalizer_get_enabled (RBEqualizer *equalizer)
+{
+    return equalizer->priv->enabled;
+}
+
+
+static void
+on_enable_button_toggled (GtkToggleButton *toggle, RBEqualizer * equalizer)
+{
+    rb_equalizer_set_enabled (equalizer, gtk_toggle_button_get_active (toggle));
+}
+
+
+static void
+on_delete_button_clicked (GtkButton *button, RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkListStore *presets = priv->presets;
+    RBEqualizerBandsSetting *preset;
+    gchar *name;
+
+    gtk_tree_model_get (GTK_TREE_MODEL (presets), &priv->current_preset,
+                        NAME_COLUMN, &name,
+                        BANDS_SETTING_COLUMN, &preset, -1);
+
+    /* this should not happen because the delete button is made insensitive
+     * for the 'Custom' preset, but let's do the test anyway */
+    if (g_str_equal (name, CUSTOM_PRESET_NAME)) {
+        g_warning ("It's not possible to delete the %s preset\n", CUSTOM_PRESET_NAME);
+        return;
+    }
+
+    g_free (preset);
+
+    /* remove from keyfile */
+    if (priv->presets_file != NULL) {
+        g_key_file_remove_group (priv->presets_file, name, NULL);
+    }
+
+    /* update combo box */
+    if (gtk_list_store_remove (presets, &priv->current_preset)) {
+
+        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), &priv->current_preset);
+
+    } else { /* priv->current_preset has been invalidated */
+        if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (presets), &priv->current_preset)) {
+
+            gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), &priv->current_preset);
+
+        } else {
+            g_warning ("something odd happened\n");
+        }
+    }
+
+    rb_equalizer_save_presets (equalizer);
+}
+
+
+static void
+on_preset_combo_changed (GtkComboBox *combo, RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBandsSetting *preset;
+    GtkTreeIter iter;
+    gchar *name;
+
+    gtk_combo_box_get_active_iter (combo, &iter);
+    priv->current_preset = iter;
+
+    gtk_tree_model_get (GTK_TREE_MODEL(priv->presets), &priv->current_preset,
+                        BANDS_SETTING_COLUMN, &preset,
+                        NAME_COLUMN, &name,
+                        -1);
+
+    rb_debug ("Changing to: %s", name);
+
+    board_set_values (equalizer, preset);
+
+    /* Forbid the deletion of 'Custom' preset */
+    gtk_widget_set_sensitive (priv->delete_button, !g_str_equal(name, CUSTOM_PRESET_NAME));
+
+    g_signal_emit (equalizer, signals[PRESET_CHANGED], 0, name, NULL);
+}
+
+
+
+/************************************************
+ *
+ *  Save Preset Dialog
+ *
+ ************************************************/
+
+static GtkWidget * create_save_dialog (RBEqualizer *equalizer);
+
+static void
+on_save_button_clicked (GtkButton *button, RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+
+    if (!priv->save_dialog) {
+        priv->save_dialog = create_save_dialog (equalizer);
+    }
+
+    gtk_widget_set_sensitive (GTK_WIDGET (equalizer), FALSE);
+    gtk_widget_show_all (priv->save_dialog);
+}
+
+
+
+static void
+on_preset_name_combo_entry_changed (GtkComboBox *combo, RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkTreeIter iter;
+    gboolean sensitive = FALSE;
+
+    if (gtk_combo_box_get_active_iter (combo, &iter)) {
+        sensitive = TRUE;
+    } else {
+        gchar *text = gtk_combo_box_get_active_text (combo);
+        g_strstrip (text);
+        if (!g_str_equal (text, "")) {
+            sensitive = TRUE;
+        }
+        g_free (text);
+    }
+
+    gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->save_dialog),
+                                       GTK_RESPONSE_ACCEPT, sensitive);
+}
+
+
+static void on_save_dialog_response (GtkDialog *dialog, gint response_id,
+                                     RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkListStore *presets = priv->presets;
+    GtkComboBox *combo = GTK_COMBO_BOX (priv->preset_name_combo);
+    RBEqualizerBandsSetting *preset = NULL;
+    GtkTreeIter iter;
+
+    switch (response_id) {
+        case GTK_RESPONSE_ACCEPT:
+            if (gtk_combo_box_get_active_iter (combo, &iter)) {
+                /* an existing preset has been selected */
+                gtk_tree_model_get (GTK_TREE_MODEL (presets), &iter,
+                                    BANDS_SETTING_COLUMN, &preset, -1);
+            } else {
+                gchar *new_name = gtk_combo_box_get_active_text (combo);
+                g_strstrip (new_name);
+                if (!g_str_equal (new_name, "")) {  /* a valid new name was entered */
+                    GtkTreeIter *iter_old;
+
+                    rb_debug ("Saving preset: %s", new_name);
+
+                    /* handle duplicates, it's bit tricky, because of localized names */
+
+                    /* first search for an existing preset with same localized name (ie what the user sees) */
+                    iter_old = search_preset (equalizer, new_name, TRUE);
+                    if (iter_old == NULL) {
+                        GString *new_name_nonlocalized = g_string_new (new_name);
+                        gint i = 0;
+
+                        /* here, the new_name could match an existing non localized name
+                         * so we try 'new_name%d' until it doesn't already exist */
+                        iter_old = search_preset (equalizer, new_name_nonlocalized->str, FALSE);
+                        while (iter_old != NULL) {
+                            g_string_printf (new_name_nonlocalized, "%s%d", new_name, i++);
+                            rb_debug ("trying name: %s", new_name_nonlocalized->str);
+                            gtk_tree_iter_free (iter_old);
+                            iter_old = search_preset (equalizer, new_name_nonlocalized->str, FALSE);
+                        }
+
+                        /* create the new preset (filled later) */
+                        preset = g_new (RBEqualizerBandsSetting, 1);
+                        preset->nb_bands = priv->board->nb_bands;
+                        preset->gains = preset->frequencies = NULL; /* to get them allocated later */
+                        gtk_list_store_prepend (presets, &iter);
+                        gtk_list_store_set (presets, &iter,
+                                            NAME_COLUMN, (i>1)? new_name_nonlocalized->str: new_name,
+                                            LOCALIZED_NAME_COLUMN, new_name,
+                                            BANDS_SETTING_COLUMN, preset, -1);
+
+                        g_string_free (new_name_nonlocalized, (i==1) );
+
+                    } else {
+                        /* just update the localized name */
+                        gtk_tree_model_get (GTK_TREE_MODEL (presets), iter_old,
+                                            BANDS_SETTING_COLUMN, &preset, -1);
+                        gtk_list_store_set (presets, iter_old,
+                                            LOCALIZED_NAME_COLUMN, new_name);
+                        iter = *iter_old;
+                        gtk_tree_iter_free (iter_old);
+                    }
+                } else {
+                    g_free (new_name);
+                }
+            }
+            if (preset != NULL) {
+                board_fill_preset (priv->board, preset);
+                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), &iter);
+
+                rb_equalizer_save_presets (equalizer);
+
+                gtk_widget_hide (GTK_WIDGET (dialog));
+            }
+            break;
+        case GTK_RESPONSE_DELETE_EVENT:
+        case GTK_RESPONSE_REJECT:
+            gtk_widget_hide (GTK_WIDGET (dialog));
+            break;
+        default: break;
+    }
+
+    gtk_widget_set_sensitive (GTK_WIDGET (equalizer), TRUE);
+}
+
+
+static GtkWidget * create_save_dialog (RBEqualizer *equalizer) {
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkWidget *dialog;
+    GtkWidget *parent;
+    GtkWidget *hbox;
+    GtkWidget *label;
+    GtkWidget *combo;
+
+    parent = gtk_widget_get_toplevel (GTK_WIDGET (equalizer));
+    if (!GTK_WIDGET_TOPLEVEL (parent)) parent = NULL;
+    dialog = gtk_dialog_new_with_buttons ("Save preset", GTK_WINDOW (parent), GTK_DIALOG_DESTROY_WITH_PARENT,
+                                           GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+                                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+                                           NULL);
+    gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+    hbox = gtk_hbox_new (FALSE, 5);
+    label = gtk_label_new ("Name");
+    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+    combo = gtk_combo_box_entry_new_with_model (GTK_TREE_MODEL (priv->presets), LOCALIZED_NAME_COLUMN);
+    g_signal_connect (GTK_COMBO_BOX (combo), "changed",
+                      G_CALLBACK (on_preset_name_combo_entry_changed), equalizer);
+    gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
+
+    gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+    gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->vbox), hbox, TRUE, FALSE, 0);
+
+    g_signal_connect (G_OBJECT (dialog), "response",
+                      G_CALLBACK (on_save_dialog_response), equalizer);
+
+    /* prevent dialog from being destructed when hidden */
+    g_signal_connect (dialog, "delete-event",
+		      G_CALLBACK(gtk_true), NULL);
+
+    priv->preset_name_combo = combo;
+
+    return dialog;
+}
+
+
+/************************************************
+ *
+ *  Init / New
+ *
+ ************************************************/
+
+static void
+update_ruler (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GString   *ruler_text;
+
+    if (priv->ruler == NULL) { /* create */
+        priv->ruler = gtk_vbox_new (FALSE, 0);
+
+        priv->ruler_label_max  = gtk_label_new ("<small></small>");
+        gtk_label_set_use_markup (GTK_LABEL (priv->ruler_label_max), TRUE);
+        gtk_box_pack_start (GTK_BOX (priv->ruler), priv->ruler_label_max, FALSE, FALSE, 0);
+
+        priv->ruler_label_zero = gtk_label_new ("<small>0 dB</small>");
+        gtk_label_set_use_markup (GTK_LABEL (priv->ruler_label_zero), TRUE);
+        gtk_box_pack_start (GTK_BOX (priv->ruler), priv->ruler_label_zero, TRUE, TRUE, 0);
+
+        priv->ruler_label_min  = gtk_label_new ("<small></small>\n");
+        gtk_label_set_use_markup (GTK_LABEL (priv->ruler_label_min), TRUE);
+        gtk_box_pack_start (GTK_BOX (priv->ruler), priv->ruler_label_min, FALSE, FALSE, 0);
+    }
+
+    /* update */
+    ruler_text = g_string_sized_new (25);
+    g_string_printf (ruler_text, "<small>%d dB</small>\n", (gint) priv->min_gain);
+    gtk_label_set_text (GTK_LABEL (priv->ruler_label_min), ruler_text->str);
+    gtk_label_set_use_markup (GTK_LABEL (priv->ruler_label_min), TRUE);
+
+    /* since [min-max] is not necesssarily centered around 0,
+     * we need to adapt the "0" label position accordlingly */
+    gtk_misc_set_alignment (GTK_MISC (priv->ruler_label_zero),
+                            0.5, 1.0 + priv->min_gain/(priv->max_gain-priv->min_gain) );
+
+    g_string_printf (ruler_text, "<small>%d dB</small>", (gint) priv->max_gain);
+    gtk_label_set_text (GTK_LABEL (priv->ruler_label_max), ruler_text->str);
+    gtk_label_set_use_markup (GTK_LABEL (priv->ruler_label_max), TRUE);
+
+    g_string_free (ruler_text, TRUE);
+}
+
+
+static void
+rb_equalizer_init (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GtkCellRenderer *renderer;
+    GtkWidget *preset_box;
+
+    equalizer->priv = priv;
+
+    /* will be FALSE eventually, but this is done so the first call
+     * to rb_equalizer_set_enabled works */
+    priv->enabled = TRUE;
+    priv->block_smoothing = TRUE;
+    priv->current_behavior = priv->default_behavior = RB_EQ_BEHAVIOR_DIRECT;
+    priv->save_dialog = NULL;
+    priv->preset_name_combo = NULL;
+    priv->board = NULL;
+
+    priv->gain_end_values = NULL;
+    priv->gain_steps = NULL;
+    priv->anim_timeout = 0;
+    priv->animated = FALSE;
+
+    equalizer->priv->min_gain = -20.0;
+    equalizer->priv->max_gain = 20.0;
+
+    priv->presets = gtk_list_store_new (N_COLUMNS,
+                                        G_TYPE_STRING,
+                                        G_TYPE_STRING,
+                                        G_TYPE_POINTER /* RBEqualizerBandsSetting */);
+
+    /* Build UI */
+    preset_box = gtk_hbox_new (FALSE, 5);
+
+    priv->enabled_button = gtk_check_button_new_with_label ("on");
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_button), FALSE);
+    g_signal_connect (G_OBJECT (priv->enabled_button), "toggled",
+                      G_CALLBACK (on_enable_button_toggled), equalizer);
+    gtk_box_pack_start (GTK_BOX (preset_box), priv->enabled_button, FALSE, FALSE, 0);
+
+    priv->preset_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (priv->presets));
+    gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->preset_combo));
+    renderer = gtk_cell_renderer_text_new ();
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->preset_combo), renderer, FALSE);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (priv->preset_combo), renderer,
+                                    "text", LOCALIZED_NAME_COLUMN, NULL);
+    g_signal_connect (G_OBJECT (priv->preset_combo), "changed",
+                      G_CALLBACK (on_preset_combo_changed), equalizer);
+    gtk_box_pack_start (GTK_BOX (preset_box), priv->preset_combo, TRUE, TRUE, 0);
+
+    priv->delete_button = gtk_button_new_from_stock (GTK_STOCK_DELETE);
+    gtk_widget_set_sensitive (priv->delete_button, FALSE);
+    g_signal_connect (G_OBJECT (priv->delete_button), "clicked",
+                      G_CALLBACK (on_delete_button_clicked), equalizer);
+    gtk_box_pack_start (GTK_BOX (preset_box), priv->delete_button, FALSE, FALSE, 0);
+
+    priv->save_button = gtk_button_new_from_stock (GTK_STOCK_SAVE_AS);
+    g_signal_connect (G_OBJECT (priv->save_button), "clicked",
+                      G_CALLBACK (on_save_button_clicked), equalizer);
+    gtk_box_pack_start (GTK_BOX (preset_box), priv->save_button, FALSE, FALSE, 0);
+
+    gtk_box_pack_start (GTK_BOX (equalizer), preset_box, FALSE, FALSE, 0);
+
+    priv->board_box = gtk_hbox_new (TRUE, 5);
+
+    /* create the [min - 0 - max] dB ruler  */
+    update_ruler (equalizer);
+
+    gtk_box_pack_end (GTK_BOX (priv->board_box), priv->ruler, TRUE, TRUE, 0);
+
+    gtk_box_pack_start (GTK_BOX (equalizer), priv->board_box, TRUE, TRUE, 0);
+
+    gtk_box_set_spacing (GTK_BOX (equalizer), 10);
+}
+
+
+GtkWidget *
+rb_equalizer_new (int nb_bands, double *frequencies)
+{
+    RBEqualizer * equalizer = RB_EQUALIZER (g_object_new (rb_equalizer_get_type (), NULL));
+    GtkTreeIter iter;
+    RBEqualizerBandsSetting *custom_preset;
+
+    /* add the mandatory 'custom' preset to the preset list */
+    custom_preset = g_new (RBEqualizerBandsSetting, 1);
+    custom_preset->frequencies = frequencies;
+    custom_preset->gains = g_new0 (gdouble, nb_bands);
+    custom_preset->nb_bands = nb_bands;
+
+    gtk_list_store_prepend (equalizer->priv->presets, &iter);
+    gtk_list_store_set (equalizer->priv->presets, &iter,
+                        NAME_COLUMN, CUSTOM_PRESET_NAME,
+                        LOCALIZED_NAME_COLUMN, CUSTOM_PRESET_NAME,
+                        BANDS_SETTING_COLUMN, custom_preset, -1);
+
+    rb_debug ("init iter : %d", iter.stamp);
+    equalizer->priv->current_preset = iter;
+    equalizer->priv->custom_preset = iter;
+
+    rb_debug ("creating board");
+    equalizer->priv->board = board_create (equalizer, nb_bands, frequencies);
+
+    rb_equalizer_set_enabled  (equalizer, FALSE);
+
+
+    return GTK_WIDGET (equalizer);
+}
+
+
+void
+rb_equalizer_set_animated (RBEqualizer *equalizer, gboolean animated)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+
+    priv->animated = animated;
+
+    if (animated) {
+        int nb_bands = priv->board->nb_bands;
+        priv->gain_end_values = g_new (gdouble, nb_bands);
+        priv->gain_steps = g_new (gdouble, nb_bands);
+    } else {
+        if (priv->gain_end_values != NULL) {
+            g_free (priv->gain_end_values);
+            priv->gain_end_values = NULL;
+        }
+        if (priv->gain_steps != NULL) {
+            g_free (priv->gain_steps);
+            priv->gain_steps = NULL;
+        }
+
+    }
+}
+
+
+void
+rb_equalizer_set_default_behavior (RBEqualizer *equalizer, RBEqualizerBehavior behavior)
+{
+    equalizer->priv->default_behavior = behavior;
+}
+
+
+static void update_board_range (RBEqualizer *equalizer);
+
+void
+rb_equalizer_set_gain_range (RBEqualizer *equalizer, gdouble min_gain, gdouble max_gain)
+{
+    equalizer->priv->min_gain = min_gain;
+    equalizer->priv->max_gain = max_gain;
+
+    update_ruler (equalizer);
+    update_board_range (equalizer);
+}
+
+/***************************************
+ *
+ * Handling the board of scale widgets
+ *
+ ***************************************/
+
+
+/* smoothing behavior parameters */
+#define SMOOTH_INFLUENCE        0.04   /* the higher, the wider the area of modified scales is */
+#define SMOOTH_STRENGTH         0.15   /* the higher, the quicker the neighboring scales equals the modified one */
+#define SMOOTH_AREA_HALF_SIZE   3  /* limits the number of neighbors modified (2*half_size+1) */
+
+
+static void
+on_scale_value_changed (GtkRange *range, RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    gint *scale_number = g_object_get_data (G_OBJECT (range), "number");
+    gdouble value = gtk_range_get_value (range);
+
+    /* do nothing when animating */
+    if (priv->anim_timeout == 0) {
+        RBEqualizerBandsSetting *custom_preset;
+        gchar *name;
+
+        /* if this handler is fired by the user changing a scale, then switch to
+         * the 'Custom' preset if it's not the current */
+        if (!priv->block_smoothing) {
+
+            gtk_tree_model_get (GTK_TREE_MODEL (priv->presets), &priv->current_preset,
+                                NAME_COLUMN, &name, -1);
+
+            if (!g_str_equal (name, CUSTOM_PRESET_NAME)) {
+                rb_debug ("%s not %s preset, changing", name, CUSTOM_PRESET_NAME);
+
+                gtk_tree_model_get (GTK_TREE_MODEL (priv->presets), &priv->custom_preset,
+                                    BANDS_SETTING_COLUMN, &custom_preset, -1);
+                /* copy current values to the custom preset */
+                board_fill_preset (priv->board, custom_preset);
+
+                /* on_preset_combo_changed would try to set values too */
+                g_signal_handlers_block_by_func (priv->preset_combo, on_preset_combo_changed, equalizer);
+                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), &priv->custom_preset);
+                priv->current_preset = priv->custom_preset;
+                g_signal_handlers_unblock_by_func (priv->preset_combo, on_preset_combo_changed, equalizer);
+
+                custom_preset->gains[*scale_number] = value;
+
+            } else { /* custom_preset is already the current one */
+                gtk_tree_model_get (GTK_TREE_MODEL (priv->presets), &priv->current_preset,
+                                    BANDS_SETTING_COLUMN, &custom_preset, -1);
+                custom_preset->gains[*scale_number] = value;
+            }
+
+            if (equalizer->priv->current_behavior == RB_EQ_BEHAVIOR_SMOOTH) {
+                /* this will take care of emitting BAND_CHANGED signals */
+                board_smooth_values (equalizer, *scale_number);
+            } else {
+                g_signal_emit (equalizer, signals[BAND_CHANGED], 0, *scale_number, value, NULL);
+            }
+        } else {
+            g_signal_emit (equalizer, signals[BAND_CHANGED], 0, *scale_number, value, NULL);
+        }
+    }
+}
+
+
+static gboolean
+on_scale_pressed (GtkWidget *scale, GdkEventButton *event, RBEqualizer *equalizer)
+{
+    if (event->state & GDK_SHIFT_MASK) {
+        equalizer->priv->current_behavior = (equalizer->priv->default_behavior == RB_EQ_BEHAVIOR_SMOOTH) ?
+                                            RB_EQ_BEHAVIOR_DIRECT : RB_EQ_BEHAVIOR_SMOOTH;
+    } else {
+        equalizer->priv->current_behavior = equalizer->priv->default_behavior;
+    }
+    equalizer->priv->block_smoothing = FALSE;
+    return FALSE;
+}
+
+
+static gboolean
+on_scale_released (GtkWidget *scale, GdkEventButton *event, RBEqualizer *equalizer)
+{
+    /* avoid smoothing values on preset change */
+    equalizer->priv->block_smoothing = TRUE;
+    equalizer->priv->current_behavior = equalizer->priv->default_behavior;
+    return FALSE;
+}
+
+
+/* Creates a band scale and its companion label.
+ * The widget is a vbox. scale_widget will contains the actual GtkScale widget.
+ * */
+static GtkWidget *
+band_scale_new (double frequency,
+		gint scale_num,
+		RBEqualizer *equalizer,
+		GtkWidget **scale_widget)
+{
+    GtkWidget * box;
+    GtkWidget * scale;
+    GtkWidget * label;
+    GString *label_text;
+    gint *scale_num_ptr;
+
+    box = gtk_vbox_new (FALSE, 2);
+
+    scale = gtk_vscale_new_with_range (equalizer->priv->min_gain, equalizer->priv->max_gain, 0.1);
+    gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
+    gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
+    gtk_range_set_value (GTK_RANGE (scale), 0.0);
+    gtk_widget_set_size_request (scale, -1, 200);
+
+    /* associate to the scale its number on the board */
+    scale_num_ptr = g_new (gint, 1);
+    *scale_num_ptr = scale_num;
+    g_object_set_data_full (G_OBJECT (scale), "number", scale_num_ptr, g_free);
+
+    g_signal_connect (G_OBJECT (scale), "value-changed",
+                      G_CALLBACK (on_scale_value_changed), equalizer);
+    g_signal_connect (G_OBJECT (scale), "button-press-event",
+                      G_CALLBACK (on_scale_pressed), equalizer);
+    g_signal_connect (G_OBJECT (scale), "button-release-event",
+                      G_CALLBACK (on_scale_released), equalizer);
+
+    label_text = g_string_new ("");
+    if (frequency < 1000) {
+        g_string_printf (label_text, "<small>%d</small>", (gint)frequency);
+    } else {
+        gdouble kilohz = frequency / 1000.0;
+        /* round to 0 or 1 digit */
+        kilohz = floor (kilohz * 10.0 + 0.5) / 10.0;
+        if (floor (kilohz) == kilohz) {
+            kilohz = floor (kilohz + 0.5);
+            g_string_printf (label_text, "<small>%dk</small>", (gint)kilohz);
+        } else {
+            g_string_printf (label_text, "<small>%0.1fk</small>", kilohz);
+        }
+    }
+    label = gtk_label_new (label_text->str);
+    gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+    g_string_free (label_text, TRUE);
+
+    gtk_box_pack_start (GTK_BOX (box), scale, TRUE, TRUE, 0);
+    gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+
+    *scale_widget = scale;
+    return box;
+}
+
+
+static RBEqualizerBoard *
+board_create (RBEqualizer *equalizer, gint nb_bands, gdouble *frequencies)
+{
+    RBEqualizerBoard *board = g_new (RBEqualizerBoard, 1);
+    GtkBox *box = GTK_BOX (equalizer->priv->board_box);
+    int i;
+
+    board->nb_bands = nb_bands;
+    board->frequencies = g_memdup (frequencies, sizeof(gdouble)*nb_bands);
+    board->scales = g_new (GtkWidget *, nb_bands);
+
+    for (i=0; i<nb_bands; i++) {
+        GtkWidget *scale = band_scale_new (frequencies[i], i, equalizer, &(board->scales[i]));
+        gtk_box_pack_start (box, scale, FALSE, FALSE, 0);
+    }
+
+    return board;
+}
+
+#define BOARD_ANIMATION_STEPS           10   /* number of steps of animation */
+#define BOARD_ANIMATION_STEP_DURATION   8   /* step duration in ms */
+
+
+static gboolean
+board_animate (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBoard *board = priv->board;
+    gboolean continue_anim = FALSE;
+    gint i;
+    g_print (".");  /* XXX remove; detect runaway animations */
+    fflush (stdout);
+    for (i=0; i<board->nb_bands; i++) {
+        gdouble end_val = priv->gain_end_values[i];
+        gdouble step = priv->gain_steps[i];
+        gdouble cur_val = gtk_range_get_value (GTK_RANGE (board->scales[i]));
+
+        if (step*(cur_val+step) >= step*end_val) { /* don't go further than end value */
+            gtk_range_set_value (GTK_RANGE (board->scales[i]), end_val);
+        } else {
+            gtk_range_set_value (GTK_RANGE (board->scales[i]), cur_val+step);
+            continue_anim = TRUE;
+        }
+    }
+
+    return continue_anim;
+}
+
+static void
+animation_ended (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBoard *board = priv->board;
+    gint i;
+
+    equalizer->priv->anim_timeout = 0;
+
+    /* now send all events */
+    for (i=0; i<board->nb_bands; i++) {
+        g_signal_emit (equalizer, signals[BAND_CHANGED], 0,
+                       i, gtk_range_get_value (GTK_RANGE (board->scales[i])), NULL);
+    }
+}
+
+/* Adapt the given setting (by linear interpolation) to fit in the current board frequencies
+ * configuration (the board can count more, less bands or have different band values).
+ */
+
+static RBEqualizerBandsSetting*
+board_adapt_setting (RBEqualizerBoard *board, RBEqualizerBandsSetting *setting)
+{
+    RBEqualizerBandsSetting *new_setting;
+    gint i, j;
+    gdouble setting_freq1, setting_freq2;
+    gdouble setting_gain1, setting_gain2;
+
+    new_setting = g_new (RBEqualizerBandsSetting, 1);
+    new_setting->nb_bands = board->nb_bands;
+    new_setting->frequencies = g_new (gdouble, board->nb_bands);
+    new_setting->gains = g_new (gdouble, board->nb_bands);
+
+
+    setting_freq1 = setting->frequencies[0];
+    setting_gain1 = setting->gains[0];
+    setting_freq2 = setting->frequencies[1];
+    setting_gain2 = setting->gains[1];
+    j = 2;
+    for (i=0; i < board->nb_bands; i++) {
+        gdouble a, b;
+        new_setting->frequencies[i] = board->frequencies[i];
+
+        while (new_setting->frequencies[i] > setting_freq2 && j < setting->nb_bands) {
+            setting_freq1 = setting_freq2;
+            setting_gain1 = setting_gain2;
+            setting_freq2 = setting->frequencies[j];
+            setting_gain2 = setting->gains[j];
+            j++;
+        }
+
+        /* this is a simple linear interpolation,
+         * which is not actually valid because gain is not a linear scale */
+        if ( (new_setting->frequencies[i] >= setting->frequencies[0])
+          && (new_setting->frequencies[i] <= setting->frequencies[setting->nb_bands-1]))
+        {
+            a = (setting_freq2 - setting_freq1 != 0)?
+                (setting_gain2 - setting_gain1) / (setting_freq2 - setting_freq1) : 0;
+            b = setting_gain1 - a*setting_freq1;
+
+            new_setting->gains[i] = a * new_setting->frequencies[i] + b;
+        } else {
+            new_setting->gains[i] = 0.0;
+        }
+    }
+
+    return new_setting;
+}
+
+
+static void
+board_set_values (RBEqualizer *equalizer, RBEqualizerBandsSetting *setting)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBandsSetting *adapted_setting;
+    gint i;
+
+    if (priv->board == NULL) return;
+
+    /* cancel any running animation */
+    if ((priv->animated) && (priv->anim_timeout != 0)) {
+        g_source_remove (priv->anim_timeout);
+        priv->anim_timeout = 0;
+    }
+
+    adapted_setting = board_adapt_setting (priv->board,  setting);
+
+    for (i=0; i < priv->board->nb_bands; i++) {
+        gdouble gain = adapted_setting->gains[i];
+        if (priv->animated) {
+            /* clamp to the range of the scale */
+            priv->gain_end_values[i] =  CLAMP (gain, priv->min_gain, priv->max_gain);
+            priv->gain_steps[i] = (gain - gtk_range_get_value (GTK_RANGE (priv->board->scales[i])))
+                                  / BOARD_ANIMATION_STEPS;
+        } else {
+            gtk_range_set_value (GTK_RANGE (priv->board->scales[i]), gain);
+        }
+    }
+
+    g_free (adapted_setting);
+
+    if (priv->animated) {
+        priv->anim_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT, BOARD_ANIMATION_STEP_DURATION,
+                                                 (GSourceFunc) board_animate, equalizer,
+                                                 (GDestroyNotify) animation_ended);
+    }
+}
+
+
+/* Copies the current values stored in the scales into the given preset */
+static void
+board_fill_preset (RBEqualizerBoard *board, RBEqualizerBandsSetting *preset)
+{
+    GtkWidget **scales = board->scales;
+    gint i, nb_bands;
+
+    nb_bands = board->nb_bands;
+    if (preset->nb_bands != nb_bands && preset->gains) {
+        g_free (preset->gains);
+        preset->gains = NULL;
+    }
+    if (!preset->gains) {
+        preset->gains = g_new (gdouble, nb_bands);
+    }
+    if (preset->nb_bands != nb_bands && preset->frequencies) {
+        g_free (preset->frequencies);
+        preset->frequencies = NULL;
+    }
+    if (!preset->frequencies) {
+        preset->frequencies = g_new (gdouble, nb_bands);
+    }
+
+    preset->nb_bands = nb_bands;
+
+    for (i=0; i<preset->nb_bands; i++) {
+        preset->frequencies[i] = board->frequencies[i];
+        preset->gains[i] = gtk_range_get_value (GTK_RANGE (scales[i]));
+    }
+}
+
+
+static void
+board_smooth_values (RBEqualizer *equalizer, gint modified_scale)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBoard *board = priv->board;
+    gint i, nb_bands;
+    gdouble value, old_value, new_value, weight, distance;
+
+    value = gtk_range_get_value (GTK_RANGE (board->scales[modified_scale]));
+    nb_bands = board->nb_bands;
+    for (i=0; i<nb_bands; i++) {
+        GtkWidget *scale = board->scales[i];
+        RBEqualizerBandsSetting *preset;
+
+        if (i == modified_scale) continue;
+        if (ABS (i - modified_scale) > SMOOTH_AREA_HALF_SIZE) continue;
+
+        /* this prevents from recursing infinitely */
+        g_signal_handlers_block_by_func (scale, on_scale_value_changed, equalizer);
+
+        /* apply the smoothing operator */
+        old_value = gtk_range_get_value (GTK_RANGE (board->scales[i]));
+        distance = (gdouble) (i - modified_scale)*(i - modified_scale)/(nb_bands*nb_bands);
+        weight = SMOOTH_STRENGTH * exp (-distance / SMOOTH_INFLUENCE);
+        new_value = weight*value + (1.0-weight)*old_value;
+
+        gtk_range_set_value (GTK_RANGE (scale), new_value);
+
+        /* save the value in the current_preset (actually, it's also the CUSTOM_PRESET) */
+        gtk_tree_model_get (GTK_TREE_MODEL (priv->presets), &priv->current_preset,
+                                BANDS_SETTING_COLUMN, &preset, -1);
+        preset->gains[i] = new_value;
+
+        /* unblock the handler */
+        g_signal_handlers_unblock_by_func (scale, on_scale_value_changed, equalizer);
+
+        g_signal_emit (equalizer, signals[BAND_CHANGED], 0, i, new_value, NULL);
+    }
+}
+
+
+static void
+update_board_range (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    RBEqualizerBoard *board = priv->board;
+    gint i, nb_bands;
+
+    nb_bands = board->nb_bands;
+    for (i=0; i<nb_bands; i++) {
+        gtk_range_set_range (GTK_RANGE (board->scales[i]), priv->min_gain, priv->max_gain);
+    }
+}
+
+
+
+/************************************************
+ *
+ * Loading / exporting to Keyfile
+ *
+ ************************************************/
+
+static void
+print_preset (RBEqualizerBandsSetting *preset)
+{
+    gint i, nbands;
+
+    nbands = preset->nb_bands;
+
+    rb_debug ("  frequencies: ");
+    for (i=0; i<nbands; i++) {
+        rb_debug ("%0.0f, ", preset->frequencies[i]);
+    }
+    rb_debug (" -.");
+
+    rb_debug ("  gains: ");
+    for (i=0; i<nbands; i++) {
+        rb_debug ("%.1f, ", preset->gains[i]);
+    }
+    rb_debug (" -.\n");
+}
+
+
+gboolean
+rb_equalizer_set_presets_file (RBEqualizer *equalizer, const char *filename)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    GKeyFile *presets_file;
+    gboolean load_ok;
+
+    if (priv->presets_file != NULL) {
+        rb_equalizer_save_presets (equalizer);
+        g_key_file_free (priv->presets_file);
+    }
+
+    presets_file = g_key_file_new ();
+    g_key_file_set_list_separator(presets_file, ';');
+
+    load_ok = g_key_file_load_from_file (presets_file, filename,
+                                         G_KEY_FILE_KEEP_TRANSLATIONS,
+                                         NULL);
+
+    priv->presets_file = presets_file;
+    priv->presets_filename = (gchar *)filename;
+
+    if (load_ok) {
+        GtkTreeIter iter;
+        GtkTreeIter *iter_custom;
+        gchar **groups, **group;
+        gsize nb_presets;
+        groups = g_key_file_get_groups (presets_file, &nb_presets);
+        rb_debug ("found %d presets", (int)nb_presets);
+
+        group = groups;
+        while (*group) {
+            gchar *name, *loc_name;
+            gsize n_freqs, n_gains;
+            gdouble * frequencies;
+            gdouble * gains;
+            RBEqualizerBandsSetting *preset;
+
+            rb_debug ("Loading preset: %s", *group);
+            name = g_strdup (*group);
+            /* retrieve the translated name, if presents */
+            loc_name = g_key_file_get_locale_string (presets_file, *group, "name", NULL, NULL);
+            if (!loc_name) {
+                loc_name = name;
+            }
+
+            rb_debug ("  preset name: %s", loc_name);
+
+            frequencies = g_key_file_get_double_list (presets_file, *group, "frequencies", &n_freqs, NULL);
+            gains = g_key_file_get_double_list (presets_file, *group, "gains", &n_gains, NULL);
+
+            if ((n_gains == n_freqs)&&(n_gains>0)) {
+                preset = g_malloc (sizeof (RBEqualizerBandsSetting));
+                preset->nb_bands = n_freqs;
+                preset->frequencies = frequencies;
+                preset->gains = gains;
+
+                print_preset (preset);
+
+                if (g_str_equal (name, CUSTOM_PRESET_NAME)) {
+                    /* 'Custom' preset already exists */
+                    iter_custom = search_preset (equalizer, CUSTOM_PRESET_NAME, FALSE);
+                    if (iter_custom == NULL) {
+                        g_warning ("Could not find %s preset. It should not happen.\n", CUSTOM_PRESET_NAME);
+                    }
+                    gtk_list_store_set (priv->presets, iter_custom,
+                                        BANDS_SETTING_COLUMN, (gpointer)preset, -1);
+                    gtk_tree_iter_free (iter_custom);
+                } else {
+                    gtk_list_store_append (priv->presets, &iter);
+                    gtk_list_store_set (priv->presets, &iter,
+                                        NAME_COLUMN, name,
+                                        LOCALIZED_NAME_COLUMN, loc_name,
+                                        BANDS_SETTING_COLUMN, (gpointer)preset, -1);
+                }
+
+            } else {
+                g_warning    ("preset [%s] has not the same number of gain and frequency values", name);
+                g_free (frequencies);
+                g_free (gains);
+                g_free (name);
+                if (loc_name != name)
+                    g_free (loc_name);
+            }
+
+            group++;
+        } /* while group */
+
+        g_strfreev (groups);
+
+        /* set current preset to last read */
+        priv->current_preset = iter;
+        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->preset_combo), &priv->current_preset);
+
+        /* the model has surely changed, so update custom plugin iter */
+        iter_custom = search_preset (equalizer, CUSTOM_PRESET_NAME, FALSE);
+        priv->custom_preset = *iter_custom;
+        gtk_tree_iter_free (iter_custom);
+
+    } else {
+        g_warning ("could not load preset file %s\n", filename);
+    }
+
+    rb_debug ("done loading");
+    return load_ok;
+}
+
+
+gboolean
+rb_equalizer_save_presets (RBEqualizer *equalizer)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+
+    if (priv->presets_filename != NULL) {
+        return rb_equalizer_export_presets (equalizer, priv->presets_filename);
+    } else {
+        return FALSE;
+    }
+}
+
+
+static gboolean
+save_preset_foreach (GtkTreeModel *model,
+		     GtkTreePath *path,
+		     GtkTreeIter *iter,
+		     GKeyFile *file)
+{
+    RBEqualizerBandsSetting *preset;
+    gchar *name;
+    gchar *loc_name;
+
+    gtk_tree_model_get (model, iter,
+                        NAME_COLUMN,           &name,
+                        LOCALIZED_NAME_COLUMN, &loc_name,
+                        BANDS_SETTING_COLUMN,  &preset, -1);
+
+    if (!g_str_equal (name, loc_name)) {
+        gchar **languages = (gchar**) g_get_language_names ();
+        g_key_file_set_locale_string (file, name, "name", languages[0], loc_name);
+    }
+
+    rb_debug ("   save preset: %s (%d bands)", name, preset->nb_bands);
+
+    g_key_file_set_double_list (file, name, "frequencies", preset->frequencies, preset->nb_bands);
+    g_key_file_set_double_list (file, name, "gains", preset->gains, preset->nb_bands);
+
+    return FALSE;
+}
+
+
+gboolean
+rb_equalizer_export_presets (RBEqualizer *equalizer, const gchar *filename)
+{
+    RBEqualizerPrivate *priv = RB_EQUALIZER_GET_PRIVATE (equalizer);
+    gboolean export_ok = FALSE;
+    FILE *file;
+
+    if (!(file = fopen (filename, "w"))) {
+        g_warning ("Could not open file: %s", filename);
+    } else {
+        gchar *data;
+        GKeyFile * kfile;
+
+        if (priv->presets_file != NULL) {
+            kfile = priv->presets_file;
+        } else {
+            kfile = g_key_file_new ();
+            g_key_file_set_list_separator(kfile, ';');
+        }
+
+        if (priv->presets != NULL) {
+            gtk_tree_model_foreach (GTK_TREE_MODEL (priv->presets),
+                                    (GtkTreeModelForeachFunc) save_preset_foreach, kfile);
+            data = g_key_file_to_data (kfile, NULL, NULL);
+            fputs (data, file);
+            fclose (file);
+            g_free (data);
+            export_ok = TRUE;
+        } else {
+            fclose (file);
+        }
+
+    }
+    return export_ok;
+}
Index: plugins/equalizer/eq-config.ini
===================================================================
--- plugins/equalizer/eq-config.ini	(revision 0)
+++ plugins/equalizer/eq-config.ini	(revision 0)
@@ -0,0 +1,10 @@
+
+[UI]
+enabled=true
+animated=true
+behavior=direct
+current_preset=Slope
+
+[Bands]
+filter=ladspa-tap-equalizer
+frequencies=32; 64; 125; 250; 500; 1000; 2000; 4000; 8000; 16000
Index: plugins/equalizer/eq-presets.ini
===================================================================
--- plugins/equalizer/eq-presets.ini	(revision 0)
+++ plugins/equalizer/eq-presets.ini	(revision 0)
@@ -0,0 +1,26 @@
+
+[no-effect]
+name[fr]=Aucun effet
+frequencies=20;20000;
+gains=0;0;
+name[fr_FR.UTF-8]=Aucun effet
+
+[Slope]
+name[fr]=Rampe
+frequencies=20;20000;
+gains=-10;10;
+name[fr_FR.UTF-8]=Rampe
+
+[Headphones]
+name[fr]=Écouteurs
+frequencies=50;300;
+gains=10;2;
+name[fr_FR.UTF-8]=Écouteurs
+
+[Roller-Coaster]
+frequencies=20;32.757874139081281;53.653915905594509;87.879411215215811;143.93713460023037;235.75372695871738;386.13954577664987;632.45553203367558;1035.8949358462417;1696.6857964881431;2778.9909887462732;4551.6918521495727;7455.187440629873;12210.804593170644;19999.999999999978;
+gains=12.093023255813954;8.9034842951267787;4.8837209302325579;3.7209302325581395;1.1627906976744184;6.279069767441861;10.465116279069766;9.5348837209302317;7.441860465116279;4.6511627906976738;0.93023255813953432;0.23255813953488413;0.93023255813953432;2.3255813953488369;0;
+
+[Custom]
+frequencies=100;200;400;1000;3000;6000;12000;15000;
+gains=0;0;0;20;0;-30.11834319526627;0;0;
Index: plugins/equalizer/rb-equalizer.h
===================================================================
--- plugins/equalizer/rb-equalizer.h	(revision 0)
+++ plugins/equalizer/rb-equalizer.h	(revision 0)
@@ -0,0 +1,126 @@
+/*
+ * RBEqualizer - Composite widget for controlling an audio equalizer
+ * 
+ * Copyright (C) 2006-2007 - Christophe Dehais <christophe.dehais@gmail.com>
+ *
+ * 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, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ */
+
+
+
+#ifndef __RB_EQUALIZER_H__
+#define __RB_EQUALIZER_H__
+
+#include <gtk/gtkvbox.h>
+#include <gtk/gtkliststore.h>
+
+
+G_BEGIN_DECLS
+
+#define RB_TYPE_EQUALIZER                  (rb_equalizer_get_type ())
+#define RB_EQUALIZER(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), RB_TYPE_EQUALIZER, RBEqualizer))
+#define RB_EQUALIZER_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), RB_TYPE_EQUALIZER, RBEqualizerClass))
+#define RB_IS_EQUALIZER(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RB_TYPE_EQUALIZER))
+#define RB_IS_EQUALIZER_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), RB_TYPE_EQUALIZER))
+#define RB_EQUALIZER_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), RB_TYPE_EQUALIZER, RBEqualizerClass))
+
+
+typedef struct _RBEqualizer        RBEqualizer;
+typedef struct _RBEqualizerClass   RBEqualizerClass;
+typedef struct _RBEqualizerPrivate RBEqualizerPrivate;
+
+
+
+
+struct _RBEqualizer
+{
+    GtkVBox box;
+    
+    /*< private >*/
+    RBEqualizerPrivate* priv;
+};
+
+struct _RBEqualizerClass
+{
+    GtkVBoxClass parent_class;
+
+    /* signals */
+    void (*band_changed) (RBEqualizer *e, guint band_num, gdouble new_value, gpointer user_data);
+    void (*enabled_toggled) (RBEqualizer *e, gboolean enabled, gpointer user_data);
+    void (*preset_changed) (RBEqualizer *e, gchar *name, gpointer user_data);
+};
+
+typedef struct _RBEqualizerBandsSetting {
+    gint nb_bands;
+    gdouble *frequencies;
+    gdouble *gains;
+} RBEqualizerBandsSetting;
+
+typedef enum {
+    RB_EQ_BEHAVIOR_SMOOTH = 1,
+    RB_EQ_BEHAVIOR_DIRECT,
+} RBEqualizerBehavior;
+
+GType rb_equalizer_get_type (void);
+
+/* Creates a new equalizer.
+ * It's board is made of nb_bands scale widget controlling one frequency band,
+ * as specified by the increasing order sorted array frequencies.
+ * Presets defining different bands will be interpolated. */
+GtkWidget * rb_equalizer_new (int nb_bands, double *frequencies);
+
+/* assumes min < 0 and max > 0 */
+void rb_equalizer_set_gain_range (RBEqualizer *equalizer, gdouble gain_min, gdouble gain_max);
+
+
+/* Do we want to animate preset change ? 
+ * Default is FALSE */
+void rb_equalizer_set_animated (RBEqualizer *equalizer, gboolean animated);
+
+/* When in 'smooth mode', moving a scale will also move its surrounding scales,
+ * actually smoothing the global equalizer curve.
+ * In 'direct mode', one only move one scale at a time.
+ * The opposite behavior of the default one is accessible by pressing the Shift modifier.
+ * Initially 'smooth mode' is the default behavior
+ */
+void rb_equalizer_set_default_behavior (RBEqualizer *equalizer, RBEqualizerBehavior behavior);
+
+
+gboolean rb_equalizer_get_enabled (RBEqualizer *equalizer);
+
+void rb_equalizer_set_enabled (RBEqualizer *equalizer, gboolean enabled);
+
+
+void rb_equalizer_set_active_preset (RBEqualizer *equalizer, const gchar *name);
+
+void rb_equalizer_set_active_preset_localized (RBEqualizer *equalizer, const gchar *name);
+
+/* should be freed after use */
+RBEqualizerBandsSetting * rb_equalizer_get_current_setting (RBEqualizer *equalizer);
+
+
+/* Loading / saving / exporting to presets to file */
+gboolean rb_equalizer_set_presets_file (RBEqualizer *equalizer, const char *presets_file);
+
+gboolean rb_equalizer_save_presets (RBEqualizer *equalizer);
+
+gboolean rb_equalizer_export_presets (RBEqualizer *equalizer, const gchar *presets_file);
+
+
+
+
+G_END_DECLS
+
+#endif
Index: plugins/equalizer/Makefile.am
===================================================================
--- plugins/equalizer/Makefile.am	(revision 0)
+++ plugins/equalizer/Makefile.am	(revision 0)
@@ -0,0 +1,103 @@
+NULL =
+
+plugindir = $(PLUGINDIR)/equalizer
+plugin_LTLIBRARIES = libequalizer.la
+
+libequalizer_la_SOURCES = \
+	rb-equalizer-plugin.c \
+	rb-equalizer.c
+
+libequalizer_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+
+INCLUDES = 						\
+        -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib                        	\
+	-I$(top_srcdir)/metadata                       	\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/rhythmdb                       	\
+	-I$(top_srcdir)/widgets                    	\
+	-I$(top_srcdir)/sources                    	\
+	-I$(top_srcdir)/iradio                    	\
+	-I$(top_srcdir)/podcast                    	\
+	-I$(top_srcdir)/plugins				\
+	-I$(top_srcdir)/shell				\
+	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
+	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
+	-DDATADIR=\""$(datadir)"\"			\
+	$(RHYTHMBOX_CFLAGS)				\
+	$(WARN_CFLAGS)					\
+	-D_XOPEN_SOURCE -D_BSD_SOURCE
+
+gladedir = $(plugindir)
+glade_DATA = equalizer-prefs.glade
+
+plugin_in_files = equalizer.rb-plugin.desktop.in
+
+%.rb-plugin: %.rb-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+BUILT_SOURCES =							\
+	$(plugin_in_files:.rb-plugin.desktop.in=.rb-plugin) 	\
+	$(NULL)
+
+plugin_DATA = 			\
+	$(BUILT_SOURCES)	\
+	$(NULL)
+
+EXTRA_DIST = $(glade_DATA) $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+NULL =
+
+plugindir = $(PLUGINDIR)/equalizer
+plugin_LTLIBRARIES = libequalizer.la
+
+libequalizer_la_SOURCES = \
+	rb-equalizer-plugin.c \
+	rb-equalizer.c
+
+libequalizer_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
+
+INCLUDES = 						\
+        -DGNOMELOCALEDIR=\""$(datadir)/locale"\"        \
+	-DG_LOG_DOMAIN=\"Rhythmbox\"		 	\
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/lib                        	\
+	-I$(top_srcdir)/metadata                       	\
+	-I$(top_srcdir)/backends			\
+	-I$(top_srcdir)/rhythmdb                       	\
+	-I$(top_srcdir)/widgets                    	\
+	-I$(top_srcdir)/sources                    	\
+	-I$(top_srcdir)/iradio                    	\
+	-I$(top_srcdir)/podcast                    	\
+	-I$(top_srcdir)/plugins				\
+	-I$(top_srcdir)/shell				\
+	-DPIXMAP_DIR=\""$(datadir)/pixmaps"\"		\
+	-DSHARE_DIR=\"$(pkgdatadir)\"                   \
+	-DDATADIR=\""$(datadir)"\"			\
+	$(RHYTHMBOX_CFLAGS)				\
+	$(WARN_CFLAGS)					\
+	-D_XOPEN_SOURCE -D_BSD_SOURCE
+
+gladedir = $(plugindir)
+glade_DATA = equalizer-prefs.glade
+
+plugin_in_files = equalizer.rb-plugin.desktop.in
+
+%.rb-plugin: %.rb-plugin.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*po) ; $(INTLTOOL_MERGE) $(top_srcdir)/po $< $@ -d -u -c $(top_builddir)/po/.intltool-merge-cache
+
+BUILT_SOURCES =							\
+	$(plugin_in_files:.rb-plugin.desktop.in=.rb-plugin) 	\
+	$(NULL)
+
+plugin_DATA = 			\
+	$(BUILT_SOURCES)	\
+	$(NULL)
+
+EXTRA_DIST = $(glade_DATA) $(plugin_in_files)
+
+CLEANFILES = $(plugin_DATA)
+DISTCLEANFILES = $(plugin_DATA)
+NULL =
Index: plugins/equalizer/equalizer-prefs.glade
===================================================================
--- plugins/equalizer/equalizer-prefs.glade	(revision 0)
+++ plugins/equalizer/equalizer-prefs.glade	(revision 0)
@@ -0,0 +1,682 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">window1</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="focus_on_map">True</property>
+  <property name="urgency_hint">False</property>
+
+  <child>
+    <widget class="GtkNotebook" id="equalizer_prefs_notebook">
+      <property name="visible">True</property>
+      <property name="can_focus">True</property>
+      <property name="show_tabs">True</property>
+      <property name="show_border">True</property>
+      <property name="tab_pos">GTK_POS_TOP</property>
+      <property name="scrollable">False</property>
+      <property name="enable_popup">False</property>
+
+      <child>
+	<widget class="GtkVBox" id="tab1_vbox">
+	  <property name="border_width">5</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">2</property>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment10">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">5</property>
+	      <property name="bottom_padding">5</property>
+	      <property name="left_padding">0</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="behavior_section_label">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Behavior&lt;/b&gt;</property>
+		  <property name="use_underline">True</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">5</property>
+		  <property name="ypad">3</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment1">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">15</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkVBox" id="vbox2">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label6">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Default curve control</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">5</property>
+		      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		      <property name="width_chars">-1</property>
+		      <property name="single_line_mode">False</property>
+		      <property name="angle">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkAlignment" id="alignment8">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">0</property>
+		      <property name="bottom_padding">0</property>
+		      <property name="left_padding">15</property>
+		      <property name="right_padding">0</property>
+
+		      <child>
+			<widget class="GtkVBox" id="vbox6">
+			  <property name="visible">True</property>
+			  <property name="homogeneous">False</property>
+			  <property name="spacing">0</property>
+
+			  <child>
+			    <widget class="GtkRadioButton" id="smooth_update_radio">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="label" translatable="yes">Smooth the curve locally </property>
+			      <property name="use_underline">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			      <property name="active">False</property>
+			      <property name="inconsistent">False</property>
+			      <property name="draw_indicator">True</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkRadioButton" id="direct_update_rodio">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="label" translatable="yes">Change only a single band</property>
+			      <property name="use_underline">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			      <property name="active">False</property>
+			      <property name="inconsistent">False</property>
+			      <property name="draw_indicator">True</property>
+			      <property name="group">smooth_update_radio</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkAlignment" id="alignment13">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">0</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">0</property>
+		      <property name="bottom_padding">7</property>
+		      <property name="left_padding">0</property>
+		      <property name="right_padding">0</property>
+
+		      <child>
+			<widget class="GtkHBox" id="hbox17">
+			  <property name="visible">True</property>
+			  <property name="homogeneous">False</property>
+			  <property name="spacing">0</property>
+
+			  <child>
+			    <widget class="GtkImage" id="image1">
+			      <property name="visible">True</property>
+			      <property name="stock">gtk-dialog-info</property>
+			      <property name="icon_size">4</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">10</property>
+			      <property name="ypad">0</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkLabel" id="label22">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">[Ctrl] key switches to the other behavior</property>
+			      <property name="use_underline">False</property>
+			      <property name="use_markup">False</property>
+			      <property name="justify">GTK_JUSTIFY_LEFT</property>
+			      <property name="wrap">False</property>
+			      <property name="selectable">False</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">0</property>
+			      <property name="ypad">5</property>
+			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			      <property name="width_chars">-1</property>
+			      <property name="single_line_mode">False</property>
+			      <property name="angle">0</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckButton" id="enable_animation_check">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Animate preset change</property>
+		      <property name="use_underline">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">False</property>
+		      <property name="inconsistent">False</property>
+		      <property name="draw_indicator">True</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment9">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">10</property>
+	      <property name="bottom_padding">5</property>
+	      <property name="left_padding">0</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="genre_section_label">
+		  <property name="label" translatable="yes">&lt;b&gt;Automatic preset switch&lt;/b&gt;</property>
+		  <property name="use_underline">True</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">5</property>
+		  <property name="ypad">3</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment3">
+	      <property name="visible">True</property>
+	      <property name="sensitive">False</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">15</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label11">
+		  <property name="label" translatable="yes">Automatically change the current preset according to some specified criterion like artist, genre, down to a particular track ?
+
+Here would come some UI to specify those rules.</property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">True</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0</property>
+		  <property name="xpad">5</property>
+		  <property name="ypad">5</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="tab_expand">False</property>
+	  <property name="tab_fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkLabel" id="label11">
+	  <property name="visible">True</property>
+	  <property name="label" translatable="yes">Main</property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">False</property>
+	  <property name="selectable">False</property>
+	  <property name="xalign">0.5</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
+	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	  <property name="width_chars">-1</property>
+	  <property name="single_line_mode">False</property>
+	  <property name="angle">0</property>
+	</widget>
+	<packing>
+	  <property name="type">tab</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkVBox" id="tab2_vbox">
+	  <property name="border_width">5</property>
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">2</property>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment11">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">5</property>
+	      <property name="bottom_padding">5</property>
+	      <property name="left_padding">0</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="filter_selection_label">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Equalizer Filter&lt;/b&gt;</property>
+		  <property name="use_underline">True</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">5</property>
+		  <property name="ypad">3</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkHBox" id="hbox16">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label20">
+		  <property name="label" translatable="yes"></property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkAlignment" id="filter_combo_alignement">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">1</property>
+		  <property name="yscale">1</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">0</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <placeholder/>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkLabel" id="label21">
+		  <property name="label" translatable="yes"></property>
+		  <property name="use_underline">False</property>
+		  <property name="use_markup">False</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment12">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">10</property>
+	      <property name="bottom_padding">5</property>
+	      <property name="left_padding">0</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkLabel" id="bands_section_label">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;Bands&lt;/b&gt;</property>
+		  <property name="use_underline">True</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">5</property>
+		  <property name="ypad">3</property>
+		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		  <property name="width_chars">-1</property>
+		  <property name="single_line_mode">False</property>
+		  <property name="angle">0</property>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkAlignment" id="alignment6">
+	      <property name="visible">True</property>
+	      <property name="xalign">0.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xscale">1</property>
+	      <property name="yscale">1</property>
+	      <property name="top_padding">0</property>
+	      <property name="bottom_padding">0</property>
+	      <property name="left_padding">15</property>
+	      <property name="right_padding">0</property>
+
+	      <child>
+		<widget class="GtkVBox" id="vbox4">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">0</property>
+
+		  <child>
+		    <widget class="GtkCheckButton" id="override_bands_check">
+		      <property name="visible">True</property>
+		      <property name="sensitive">False</property>
+		      <property name="can_focus">True</property>
+		      <property name="label" translatable="yes">Override Default</property>
+		      <property name="use_underline">True</property>
+		      <property name="relief">GTK_RELIEF_NORMAL</property>
+		      <property name="focus_on_click">True</property>
+		      <property name="active">False</property>
+		      <property name="inconsistent">False</property>
+		      <property name="draw_indicator">True</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkAlignment" id="alignment7">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">0</property>
+		      <property name="bottom_padding">10</property>
+		      <property name="left_padding">20</property>
+		      <property name="right_padding">25</property>
+
+		      <child>
+			<widget class="GtkScrolledWindow" id="scrolledwindow1">
+			  <property name="border_width">2</property>
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+			  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			  <property name="shadow_type">GTK_SHADOW_NONE</property>
+			  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			  <child>
+			    <widget class="GtkViewport" id="filter_ui_container">
+			      <property name="visible">True</property>
+			      <property name="shadow_type">GTK_SHADOW_IN</property>
+
+			      <child>
+				<placeholder/>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="tab_expand">False</property>
+	  <property name="tab_fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkLabel" id="label12">
+	  <property name="visible">True</property>
+	  <property name="label" translatable="yes">Advanced</property>
+	  <property name="use_underline">False</property>
+	  <property name="use_markup">False</property>
+	  <property name="justify">GTK_JUSTIFY_LEFT</property>
+	  <property name="wrap">False</property>
+	  <property name="selectable">False</property>
+	  <property name="xalign">0.5</property>
+	  <property name="yalign">0.5</property>
+	  <property name="xpad">0</property>
+	  <property name="ypad">0</property>
+	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+	  <property name="width_chars">-1</property>
+	  <property name="single_line_mode">False</property>
+	  <property name="angle">0</property>
+	</widget>
+	<packing>
+	  <property name="type">tab</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
Index: plugins/equalizer/equalizer.rb-plugin.desktop.in
===================================================================
--- plugins/equalizer/equalizer.rb-plugin.desktop.in	(revision 0)
+++ plugins/equalizer/equalizer.rb-plugin.desktop.in	(revision 0)
@@ -0,0 +1,9 @@
+[RB Plugin]
+Module=equalizer
+IAge=1
+_Name=Equalizer
+_Description=Adds a graphical equalizer to tune output frequencies
+Icon=rb-equalizer.svg
+Authors=Christophe Dehais <christophe.dehais@gmail.com>
+Copyright=Copyright © 2007 Christophe Dehais
+Website=http://www.rhythmbox.org/
Index: plugins/Makefile.am
===================================================================
--- plugins/Makefile.am	(revision 5823)
+++ plugins/Makefile.am	(working copy)
@@ -6,6 +6,7 @@
 	mmkeys						\
 	power-manager					\
 	sample						\
+	equalizer					\
 	visualizer
 
 if WITH_AUDIOSCROBBLER
