[SCM] calf/master: + First attempt at preset handling + GUI refactoring to enable preset handling and for further expansion

js at users.alioth.debian.org js at users.alioth.debian.org
Tue May 7 15:36:45 UTC 2013


The following commit has been merged in the master branch:
commit 20a39faaaf6d2463a08bedc34b3d4501ff2779fb
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Mon Dec 17 00:04:32 2007 +0000

    + First attempt at preset handling
    + GUI refactoring to enable preset handling and for further expansion
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@26 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/calf.glade b/calf.glade
new file mode 100644
index 0000000..8745abe
--- /dev/null
+++ b/calf.glade
@@ -0,0 +1,130 @@
+<?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="store_preset">
+  <property name="border_width">10</property>
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Store preset</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">True</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="icon_name">gtk-add</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_DIALOG</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="GtkVBox" id="vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">10</property>
+
+      <child>
+	<widget class="GtkHBox" id="hbox1">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">0</property>
+
+	  <child>
+	    <widget class="GtkLabel" id="label1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes"><b>_Preset name: </b></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.5</property>
+	      <property name="yalign">0.5</property>
+	      <property name="xpad">12</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">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkEntry" id="preset_name">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="editable">True</property>
+	      <property name="visibility">True</property>
+	      <property name="max_length">0</property>
+	      <property name="text" translatable="yes"></property>
+	      <property name="has_frame">True</property>
+	      <property name="invisible_char">●</property>
+	      <property name="activates_default">False</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHButtonBox" id="hbuttonbox1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+	  <property name="spacing">6</property>
+
+	  <child>
+	    <widget class="GtkButton" id="cancel_button">
+	      <property agent="glademm" name="cxx_visibility">public</property>
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="ok_button">
+	      <property agent="glademm" name="cxx_visibility">public</property>
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="has_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-ok</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/calf.gladep b/calf.gladep
new file mode 100644
index 0000000..cc776ec
--- /dev/null
+++ b/calf.gladep
@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd">
+
+<glade-project>
+  <name>Calf</name>
+  <program_name>calf</program_name>
+  <language>C++</language>
+  <gnome_support>FALSE</gnome_support>
+</glade-project>
diff --git a/configure.in b/configure.in
index d129153..1d722ee 100644
--- a/configure.in
+++ b/configure.in
@@ -53,7 +53,7 @@ AC_SUBST(JACK_DEPS_LIBS)
 
 PHAT_ENABLED="no"
 if test "$JACK_FOUND" = "yes"; then
-  PKG_CHECK_MODULES(GUI_DEPS, gtk+-2.0 >= 2.4.0, 
+  PKG_CHECK_MODULES(GUI_DEPS, gtk+-2.0 >= 2.4.0 libglade-2.0 >= 2.4.0, 
     JACK_ENABLED="yes",
     JACK_ENABLED="no (GTK+ is missing or too old)"
   )
diff --git a/src/Makefile.am b/src/Makefile.am
index 82dd7da..8c3185b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,7 @@ if USE_JACK
 AM_CXXFLAGS += -DUSE_JACK=1 $(JACK_DEPS_CFLAGS) $(GUI_DEPS_CFLAGS)
 bin_PROGRAMS += calfjackhost 
 calfjackhost_SOURCES = jackhost.cpp
-calfjackhost_LDADD = libcalfstatic.la libcalfgui.la $(JACK_DEPS_LIBS) $(GUI_DEPS_LIBS)
+calfjackhost_LDADD = libcalfgui.la libcalfstatic.la $(JACK_DEPS_LIBS) $(GUI_DEPS_LIBS)
 if OLD_JACK
 AM_CXXFLAGS += -DOLD_JACK=1
 endif
diff --git a/src/calf/giface.h b/src/calf/giface.h
index 0869687..f7d94c8 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -381,6 +381,9 @@ public:
     virtual ~audio_exception() throw () {}
 };
 
+// this should go into some utility library, perhaps
+std::string xml_escape(const std::string &src);
+
 };
 
 #endif
diff --git a/src/calf/gui.h b/src/calf/gui.h
index 8800cd2..daa198c 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -22,6 +22,7 @@
 #ifndef __CALF_GUI_H
 #define __CALF_GUI_H
 
+#include <vector>
 #include <gtk/gtk.h>
 #if USE_PHAT
 #include <phat/phatknob.h>
@@ -35,7 +36,20 @@ struct param_control
 {
     plugin_gui *gui;
     int param_no;
-    GtkWidget *label;
+    GtkWidget *label, *widget;
+    
+    param_control() { gui = NULL; param_no = -1; label = NULL; }
+    inline parameter_properties &get_props();
+    
+    virtual GtkWidget *create_label(int param_idx);
+    virtual void update_label();
+    /// called to create a widget for a control
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no)=0;
+    /// called to transfer the value from control to parameter(s)
+    virtual void get()=0;
+    /// called to transfer the value from parameter(s) to control
+    virtual void set()=0;
+    virtual ~param_control() {}
 };
 
 struct plugin_ctl_iface
@@ -48,6 +62,49 @@ struct plugin_ctl_iface
     virtual ~plugin_ctl_iface() {}
 };
 
+struct hscale_param_control: public param_control
+{
+    GtkHScale *scale;
+
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
+    virtual void get();
+    virtual void set();
+    static void hscale_value_changed(GtkHScale *widget, gpointer value);
+    static gchar *hscale_format_value(GtkScale *widget, double arg1, gpointer value);
+};
+
+struct toggle_param_control: public param_control
+{
+    GtkCheckButton *scale;
+
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
+    virtual void get();
+    virtual void set();
+    static void toggle_value_changed(GtkCheckButton *widget, gpointer value);
+};
+
+struct combo_box_param_control: public param_control
+{
+    GtkComboBox *scale;
+
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
+    virtual void get();
+    virtual void set();
+    static void combo_value_changed(GtkComboBox *widget, gpointer value);
+};
+
+#if USE_PHAT
+struct knob_param_control: public param_control
+{
+    PhatKnob *knob;
+    
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
+    virtual void get();
+    virtual void set();
+    static void knob_value_changed(PhatKnob *widget, gpointer value);
+};
+#endif
+
 class plugin_gui
 {
 protected:
@@ -56,21 +113,22 @@ public:
     GtkWidget *toplevel;
     GtkWidget *table;
     plugin_ctl_iface *plugin;
-    param_control *params;
+    std::vector<param_control *> params;
 
     plugin_gui(GtkWidget *_toplevel);
-    void create(plugin_ctl_iface *_plugin, const char *title);
-    GtkWidget *create_label(int param_idx);
-    static void hscale_value_changed(GtkHScale *widget, gpointer value);
-    static gchar *hscale_format_value(GtkScale *widget, double arg1, gpointer value);
-    static void combo_value_changed(GtkComboBox *widget, gpointer value);
-    static void toggle_value_changed(GtkCheckButton *widget, gpointer value);
+    GtkWidget *create(plugin_ctl_iface *_plugin, const char *title);
+    void refresh();
 
 #if USE_PHAT
     static void knob_value_changed(PhatKnob *widget, gpointer value);
 #endif
 };
 
+inline parameter_properties &param_control::get_props() 
+{ 
+    return  *gui->plugin->get_param_props(param_no);
+}
+
 };
 
 #endif
diff --git a/src/calf/preset.h b/src/calf/preset.h
new file mode 100644
index 0000000..e836c1f
--- /dev/null
+++ b/src/calf/preset.h
@@ -0,0 +1,58 @@
+/* Calf DSP Library
+ * Preset management
+ *
+ * Copyright (C) 2007 Krzysztof Foltman
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __CALF_PRESET_H
+#define __CALF_PRESET_H
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <ostream>
+#include "giface.h"
+
+namespace synth {
+
+struct plugin_preset
+{
+    int bank, program;
+    std::string name;
+    std::string plugin;
+    std::vector<std::string> param_names;
+    std::vector<float> values;
+    std::string blob;
+    std::string to_xml()
+    {
+        std::stringstream ss;
+        ss << "<preset bank=\"" << bank << "\" program=\"" << program << "\" plugin=\"" << plugin << "\" name=\"" << xml_escape(name) << "\">\n";
+        for (unsigned int i = 0; i < values.size(); i++) {
+            if (i < param_names.size())
+                ss << "  <param name=\"" << param_names[i] << "\" value=\"" << values[i] << "\" />\n";
+            else
+                ss << "  <param value=\"" << values[i] << "\" />\n";
+        }
+        // XXXKF I'm not writing blob here, because I don't use blobs yet anyway
+        ss << "</preset>\n";
+        return ss.str();
+    }
+};
+
+};
+
+#endif
diff --git a/src/giface.cpp b/src/giface.cpp
index 9246a94..781406c 100644
--- a/src/giface.cpp
+++ b/src/giface.cpp
@@ -130,7 +130,7 @@ static string f2s(double value)
     return buf;
 }
 
-static string rdf_escape(const string &src)
+std::string synth::xml_escape(const std::string &src)
 {
     string dest;
     for (size_t i = 0; i < src.length(); i++) {
@@ -185,8 +185,8 @@ std::string synth::generate_ladspa_rdf(const ladspa_info &info, parameter_proper
     string plugin_type = string(info.plugin_type); 
     
     rdf += "  <ladspa:" + plugin_type + " rdf:about=\"" + plugin_id + "\">\n";
-    rdf += "    <dc:creator>" + rdf_escape(info.maker) + "</dc:creator>\n";
-    rdf += "    <dc:title>" + rdf_escape(info.name) + "</dc:title>\n";
+    rdf += "    <dc:creator>" + xml_escape(info.maker) + "</dc:creator>\n";
+    rdf += "    <dc:title>" + xml_escape(info.name) + "</dc:title>\n";
     
     for (unsigned int i = 0; i < count; i++) {
         rdf += 
diff --git a/src/gui.cpp b/src/gui.cpp
index ea53361..45136f7 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -25,28 +25,98 @@
 
 using namespace synth;
 
-plugin_gui::plugin_gui(GtkWidget *_toplevel)
-: toplevel(_toplevel)
+
+/******************************** controls ********************************/
+
+GtkWidget *param_control::create_label(int param_idx)
+{
+    label = gtk_label_new ("");
+    gtk_label_set_width_chars (GTK_LABEL (label), 12);
+    gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+    return label;
+}
+
+void param_control::update_label()
+{
+    parameter_properties &props = get_props();
+    gtk_label_set_text (GTK_LABEL (label), props.to_string(gui->plugin->get_param_value(param_no)).c_str());
+}
+
+// combo box
+
+GtkWidget *combo_box_param_control::create(plugin_gui *_gui, int _param_no)
 {
+    gui = _gui;
+    param_no = _param_no;
+    
+    parameter_properties &props = get_props();
+    widget  = gtk_combo_box_new_text ();
+    for (int j = (int)props.min; j <= (int)props.max; j++)
+        gtk_combo_box_append_text (GTK_COMBO_BOX (widget), props.choices[j - (int)props.min]);
+    gtk_signal_connect (GTK_OBJECT (widget), "changed", G_CALLBACK (combo_value_changed), (gpointer)this);
     
+    return widget;
+}
+
+void combo_box_param_control::set()
+{
+    parameter_properties &props = get_props();
+    gtk_combo_box_set_active (GTK_COMBO_BOX (widget), (int)gui->plugin->get_param_value(param_no) - (int)props.min);
 }
 
-void plugin_gui::hscale_value_changed(GtkHScale *widget, gpointer value)
+void combo_box_param_control::get()
+{
+    parameter_properties &props = get_props();
+    gui->plugin->set_param_value(param_no, gtk_combo_box_get_active (GTK_COMBO_BOX(widget)) + props.min);
+}
+
+void combo_box_param_control::combo_value_changed(GtkComboBox *widget, gpointer value)
 {
     param_control *jhp = (param_control *)value;
-    plugin_ctl_iface &pif = *jhp->gui->plugin;
-    int nparam = jhp->param_no;
-    const parameter_properties &props = *pif.get_param_props(nparam);
+    jhp->get();
+}
+
+// horizontal fader
+
+GtkWidget *hscale_param_control::create(plugin_gui *_gui, int _param_no)
+{
+    gui = _gui;
+    param_no = _param_no;
+
+    gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
+    
+    widget = gtk_hscale_new_with_range (0, 1, 0.01);
+    gtk_signal_connect (GTK_OBJECT (widget), "value-changed", G_CALLBACK (hscale_value_changed), (gpointer)this);
+    gtk_signal_connect (GTK_OBJECT (widget), "format-value", G_CALLBACK (hscale_format_value), (gpointer)this);
+    gtk_widget_set_size_request (widget, 200, -1);
+
+    return widget;
+}
+
+void hscale_param_control::set()
+{
+    parameter_properties &props = get_props();
+    gtk_range_set_value (GTK_RANGE (widget), props.to_01 (gui->plugin->get_param_value(param_no)));
+    hscale_value_changed (GTK_HSCALE (widget), (gpointer)this);
+}
+
+void hscale_param_control::get()
+{
+    parameter_properties &props = get_props();
     float cvalue = props.from_01 (gtk_range_get_value (GTK_RANGE (widget)));
-    pif.set_param_value(nparam, cvalue);
-    // gtk_label_set_text (GTK_LABEL (jhp->label), props.to_string(cvalue).c_str());
+    gui->plugin->set_param_value(param_no, cvalue);
 }
 
-gchar *plugin_gui::hscale_format_value(GtkScale *widget, double arg1, gpointer value)
+void hscale_param_control::hscale_value_changed(GtkHScale *widget, gpointer value)
 {
-    param_control *jhp = (param_control *)value;
-    plugin_ctl_iface &pif = *jhp->gui->plugin;
-    const parameter_properties &props = *pif.get_param_props (jhp->param_no);
+    hscale_param_control *jhp = (hscale_param_control *)value;
+    jhp->get();
+}
+
+gchar *hscale_param_control::hscale_format_value(GtkScale *widget, double arg1, gpointer value)
+{
+    hscale_param_control *jhp = (hscale_param_control *)value;
+    const parameter_properties &props = jhp->get_props();
     float cvalue = props.from_01 (arg1);
     
     // for testing
@@ -54,111 +124,146 @@ gchar *plugin_gui::hscale_format_value(GtkScale *widget, double arg1, gpointer v
     return g_strdup (props.to_string (cvalue).c_str());
 }
 
-void plugin_gui::combo_value_changed(GtkComboBox *widget, gpointer value)
+// check box
+
+GtkWidget *toggle_param_control::create(plugin_gui *_gui, int _param_no)
 {
-    param_control *jhp = (param_control *)value;
-    plugin_ctl_iface &pif = *jhp->gui->plugin;
-    int nparam = jhp->param_no;
-    pif.set_param_value(nparam, gtk_combo_box_get_active (widget) + pif.get_param_props (jhp->param_no)->min);
+    gui = _gui;
+    param_no = _param_no;
+    
+    widget  = gtk_check_button_new ();
+    gtk_signal_connect (GTK_OBJECT (widget), "toggled", G_CALLBACK (toggle_value_changed), (gpointer)this);
+    return widget;
 }
 
-void plugin_gui::toggle_value_changed(GtkCheckButton *widget, gpointer value)
+void toggle_param_control::toggle_value_changed(GtkCheckButton *widget, gpointer value)
 {
     param_control *jhp = (param_control *)value;
-    plugin_ctl_iface &pif = *jhp->gui->plugin;
-    int nparam = jhp->param_no;
-    pif.set_param_value(nparam, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) + pif.get_param_props (jhp->param_no)->min);
+    jhp->get();
+}
+
+void toggle_param_control::get()
+{
+    const parameter_properties &props = get_props();
+    plugin_ctl_iface &pif = *gui->plugin;
+    pif.set_param_value(param_no, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) + props.min);
+}
+
+void toggle_param_control::set()
+{
+    const parameter_properties &props = get_props();
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), (int)gui->plugin->get_param_value(param_no) - (int)props.min);
 }
 
+// knob
+
 #if USE_PHAT
-void plugin_gui::knob_value_changed(PhatKnob *widget, gpointer value)
+GtkWidget *knob_param_control::create(plugin_gui *_gui, int _param_no)
+{
+    gui = _gui;
+    param_no = _param_no;
+    const parameter_properties &props = get_props();
+    
+    widget = phat_knob_new_with_range (props.to_01 (gui->plugin->get_param_value(param_no)), 0, 1, 0.01);
+    gtk_signal_connect(GTK_OBJECT(widget), "value-changed", G_CALLBACK(knob_value_changed), (gpointer)this);
+    return widget;
+}
+
+void knob_param_control::get()
+{
+    const parameter_properties &props = get_props();
+    float value = props.from_01(phat_knob_get_value(PHAT_KNOB(widget)));
+    gui->plugin->set_param_value(param_no, value);
+}
+
+void knob_param_control::set()
+{
+    const parameter_properties &props = get_props();
+    phat_knob_set_value(PHAT_KNOB(widget), props.to_01 (gui->plugin->get_param_value(param_no)));
+    knob_value_changed(PHAT_KNOB(widget), (gpointer)this);
+}
+
+void knob_param_control::knob_value_changed(PhatKnob *widget, gpointer value)
 {
     param_control *jhp = (param_control *)value;
     plugin_ctl_iface &pif = *jhp->gui->plugin;
-    const parameter_properties &props = *pif.get_param_props (jhp->param_no);
-    int nparam = jhp->param_no;
+    const parameter_properties &props = jhp->get_props();
     float cvalue = props.from_01 (phat_knob_get_value (widget));
-    pif.set_param_value(nparam, cvalue);
-    gtk_label_set_text (GTK_LABEL (jhp->label), props.to_string(cvalue).c_str());
+    pif.set_param_value(jhp->param_no, cvalue);
+    jhp->update_label();
 }
 #endif
 
-GtkWidget *plugin_gui::create_label(int param_idx)
+/******************************** GUI proper ********************************/
+
+plugin_gui::plugin_gui(GtkWidget *_toplevel)
+: toplevel(_toplevel)
 {
-    GtkWidget *widget  = gtk_label_new ("");
-    gtk_label_set_width_chars (GTK_LABEL (widget), 12);
-    gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
-    params[param_idx].label = widget;
-    return widget;
+    
 }
 
-void plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
+
+GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
 {
     plugin = _plugin;
     param_count = plugin->get_param_count();
-    params = new param_control[param_count];
-    
+    params.resize(param_count);
     for (int i = 0; i < param_count; i++) {
-        params[i].gui = this;
-        params[i].param_no = i;
+        params[i] = NULL;
     }
-
-    table = gtk_table_new (param_count + 1, 3, FALSE);
     
-    GtkWidget *title_label = gtk_label_new ("");
-    gtk_label_set_markup (GTK_LABEL (title_label), (std::string("<b>")+title+"</b>").c_str());
-    gtk_table_attach (GTK_TABLE (table), title_label, 0, 3, 0, 1, GTK_EXPAND, GTK_SHRINK, 2, 2);
-
+    table = gtk_table_new (param_count, 3, FALSE);
+    
     for (int i = 0; i < param_count; i++) {
+        int trow = i;
         GtkWidget *label = gtk_label_new (plugin->get_param_names()[i]);
         gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, i + 1, i + 2, GTK_FILL, GTK_FILL, 2, 2);
+        gtk_table_attach (GTK_TABLE (table), label, 0, 1, trow, trow + 1, GTK_FILL, GTK_FILL, 2, 2);
         
         parameter_properties &props = *plugin->get_param_props(i);
         
-        GtkWidget *widget;
+        GtkWidget *widget = NULL;
         
         if ((props.flags & PF_TYPEMASK) == PF_ENUM && 
             (props.flags & PF_CTLMASK) == PF_CTL_COMBO)
         {
-            widget  = gtk_combo_box_new_text ();
-            for (int j = (int)props.min; j <= (int)props.max; j++)
-                gtk_combo_box_append_text (GTK_COMBO_BOX (widget), props.choices[j - (int)props.min]);
-            gtk_combo_box_set_active (GTK_COMBO_BOX (widget), (int)plugin->get_param_value(i) - (int)props.min);
-            gtk_signal_connect (GTK_OBJECT (widget), "changed", G_CALLBACK (combo_value_changed), (gpointer)&params[i]);
-            gtk_table_attach (GTK_TABLE (table), widget, 1, 3, i + 1, i + 2, GTK_EXPAND, GTK_SHRINK, 0, 0);
+            params[i] = new combo_box_param_control();
+            widget = params[i]->create(this, i);
+            gtk_table_attach (GTK_TABLE (table), widget, 1, 3, trow, trow + 1, GTK_EXPAND, GTK_SHRINK, 0, 0);
         }
         else if ((props.flags & PF_TYPEMASK) == PF_BOOL && 
                  (props.flags & PF_CTLMASK) == PF_CTL_TOGGLE)
         {
-            widget  = gtk_check_button_new ();
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), (int)plugin->get_param_value(i) - (int)props.min);
-            gtk_signal_connect (GTK_OBJECT (widget), "toggled", G_CALLBACK (toggle_value_changed), (gpointer)&params[i]);
-            gtk_table_attach (GTK_TABLE (table), widget, 1, 3, i + 1, i + 2, GTK_EXPAND, GTK_SHRINK, 0, 0);
+            params[i] = new toggle_param_control();
+            widget = params[i]->create(this, i);
+            gtk_table_attach (GTK_TABLE (table), widget, 1, 3, trow, trow + 1, GTK_EXPAND, GTK_SHRINK, 0, 0);
         }
 #if USE_PHAT
         else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
         {
-            GtkWidget *knob = phat_knob_new_with_range (props.to_01 (plugin->get_param_value(i)), 0, 1, 0.01);
-            gtk_signal_connect (GTK_OBJECT (knob), "value-changed", G_CALLBACK (knob_value_changed), (gpointer)&params[i]);
-            gtk_table_attach (GTK_TABLE (table), knob, 1, 2, i + 1, i + 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
-            gtk_table_attach (GTK_TABLE (table), create_label(i), 2, 3, i + 1, i + 2, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), GTK_SHRINK, 0, 0);
-            knob_value_changed (PHAT_KNOB (knob), (gpointer)&params[i]);
+            params[i] = new knob_param_control();
+            widget = params[i]->create(this, i);
+            gtk_table_attach (GTK_TABLE (table), widget, 1, 2, trow, trow + 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+            gtk_table_attach (GTK_TABLE (table), params[i]->create_label(i), 2, 3, trow, trow + 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), GTK_SHRINK, 0, 0);
         }
 #endif
         else
         {
-            gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
-            
-            GtkWidget *knob = gtk_hscale_new_with_range (0, 1, 0.01);
-            gtk_signal_connect (GTK_OBJECT (knob), "value-changed", G_CALLBACK (hscale_value_changed), (gpointer)&params[i]);
-            gtk_signal_connect (GTK_OBJECT (knob), "format-value", G_CALLBACK (hscale_format_value), (gpointer)&params[i]);
-            gtk_widget_set_size_request (knob, 200, -1);
-            gtk_table_attach (GTK_TABLE (table), knob, 1, 3, i + 1, i + 2, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_SHRINK, 0, 0);
-            
-            gtk_range_set_value (GTK_RANGE (knob), props.to_01 (plugin->get_param_value(i)));
-            hscale_value_changed (GTK_HSCALE (knob), (gpointer)&params[i]);
+            params[i] = new hscale_param_control();
+            widget = params[i]->create(this, i);
+            gtk_table_attach (GTK_TABLE (table), widget, 1, 3, trow, trow + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_SHRINK, 0, 0);
         }
+        params[i]->set();
     }
-    gtk_container_add (GTK_CONTAINER (toplevel), table);
+    return table;
 }
+
+void plugin_gui::refresh()
+{
+    for (unsigned int i = 0; i < params.size(); i++)
+    {
+        if (params[i] != NULL)
+            params[i]->set();
+    }
+}
+
diff --git a/src/jackhost.cpp b/src/jackhost.cpp
index 7a50611..50a5631 100644
--- a/src/jackhost.cpp
+++ b/src/jackhost.cpp
@@ -23,32 +23,325 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <config.h>
+#include <fcntl.h>
+#include <expat.h>
+#include <glade/glade.h>
 #include <jack/jack.h>
 #include <calf/giface.h>
 #include <calf/jackhost.h>
 #include <calf/modules.h>
 #include <calf/modules_dev.h>
 #include <calf/gui.h>
+#include <calf/preset.h>
 
 using namespace synth;
+using namespace std;
 
 // I don't need anyone to tell me this is stupid. I already know that :)
+GtkWidget *toplevel;
 plugin_gui *gui;
+GtkUIManager *ui_mgr;
+
+const char *effect_name = "flanger";
+const char *client_name = "calfhost";
+const char *input_name = "input";
+const char *output_name = "output";
+const char *midi_name = "midi";
 
 void destroy(GtkWindow *window, gpointer data)
 {
     gtk_main_quit();
 }
 
+static const char *ui_xml = 
+"<ui>\n"
+"  <menubar>\n"
+"    <menu action=\"HostMenuAction\">\n"
+"      <menuitem action=\"exit\"/>\n"
+"    </menu>\n"
+"    <menu action=\"PresetMenuAction\">\n"
+"      <menuitem action=\"store-preset\"/>\n"
+"      <separator/>\n"
+"      <placeholder name=\"presets\"/>\n"
+"    </menu>\n"
+"  </menubar>\n"
+"</ui>\n"
+;
+
+static const char *preset_pre_xml = 
+"<ui>\n"
+"  <menubar>\n"
+"    <menu action=\"PresetMenuAction\">\n"
+"      <placeholder name=\"presets\">\n";
+
+static const char *preset_post_xml = 
+"      </placeholder>\n"
+"    </menu>\n"
+"  </menubar>\n"
+"</ui>\n"
+;
+
+void exit_gui()
+{
+    gtk_widget_destroy(toplevel);
+}
+
+vector<plugin_preset> presets;
+
+string get_preset_filename()
+{
+    const char *home = getenv("HOME");
+    return string(home)+"/.calfpresets";
+}
+
+enum PresetParserState
+{
+    START,
+    LIST,
+    PRESET,
+    VALUE,
+} xml_parser_state;
+
+plugin_preset parser_preset;
+
+void xml_start_element_handler(void *user_data, const char *name, const char *attrs[])
+{
+    switch(xml_parser_state)
+    {
+    case START:
+        if (!strcmp(name, "presets")) {
+            xml_parser_state = LIST;
+            return;
+        }
+        break;
+    case LIST:
+        if (!strcmp(name, "preset")) {
+            parser_preset.bank = parser_preset.program = 0;
+            parser_preset.name = "";
+            parser_preset.plugin = "";
+            parser_preset.blob = "";
+            parser_preset.param_names.clear();
+            parser_preset.values.clear();
+            for(; *attrs; attrs += 2) {
+                if (!strcmp(attrs[0], "bank")) parser_preset.bank = atoi(attrs[1]);
+                else
+                if (!strcmp(attrs[0], "program")) parser_preset.program = atoi(attrs[1]);
+                else
+                if (!strcmp(attrs[0], "name")) parser_preset.name = attrs[1];
+                else
+                if (!strcmp(attrs[0], "plugin")) parser_preset.plugin = attrs[1];
+            }
+            xml_parser_state = PRESET;
+            return;
+        }
+        break;
+    case PRESET:
+        if (!strcmp(name, "param")) {
+            std::string name;
+            float value = 0.f;
+            for(; *attrs; attrs += 2) {
+                if (!strcmp(attrs[0], "name")) name = attrs[1];
+                else
+                if (!strcmp(attrs[0], "value")) {
+                    istringstream str(attrs[1]);
+                    str >> value;
+                }
+            }
+            parser_preset.param_names.push_back(name);
+            parser_preset.values.push_back(value);
+            xml_parser_state = VALUE;
+            return;
+        }
+        break;
+    case VALUE:
+        break;
+    }
+    g_warning("Invalid XML element: %s", name);
+}
+
+void xml_end_element_handler(void *user_data, const char *name)
+{
+    switch(xml_parser_state)
+    {
+    case START:
+        break;
+    case LIST:
+        if (!strcmp(name, "presets")) {
+            xml_parser_state = START;
+            return;
+        }
+        break;
+    case PRESET:
+        if (!strcmp(name, "preset")) {
+            presets.push_back(parser_preset);
+            xml_parser_state = LIST;
+            return;
+        }
+        break;
+    case VALUE:
+        if (!strcmp(name, "param")) {
+            xml_parser_state = PRESET;
+            return;
+        }
+        break;
+    }
+    g_warning("Invalid XML element close: %s", name);
+}
+
+void load_presets()
+{
+    xml_parser_state = START;
+    XML_Parser parser = XML_ParserCreate("UTF-8");
+    string filename = get_preset_filename();
+    int fd = open(filename.c_str(), O_RDONLY);
+    if (fd < 0) {
+        g_warning("Could not save the presets in %s", filename.c_str());
+        return;
+    }
+    XML_SetElementHandler(parser, xml_start_element_handler, xml_end_element_handler);
+    char buf[4096];
+    do
+    {
+        int len = read(fd, buf, 4096);
+        // XXXKF not an optimal error/EOF handling :)
+        if (len <= 0)
+            break;
+        XML_Parse(parser, buf, len, 0);
+    } while(1);
+    XML_Parse(parser, buf, 0, 1);
+    close(fd);
+}
+
+void save_presets()
+{
+    string xml = "<presets>\n";
+    for (unsigned int i = 0; i < presets.size(); i++)
+    {
+        xml += presets[i].to_xml();
+    }
+    xml += "</presets>";
+    string filename = get_preset_filename();
+    int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0640);
+    if (fd < 0 || ((unsigned)write(fd, xml.c_str(), xml.length()) != xml.length())) {
+        g_warning("Could not save the presets in %s", filename.c_str());
+        return;
+    }
+    close(fd);
+}
+
+GladeXML *store_preset_xml = NULL;
+GtkWidget *store_preset_dlg = NULL;
+
+void store_preset_dlg_destroy(GtkWindow *window, gpointer data)
+{
+    store_preset_dlg = NULL;
+}
+
+void store_preset_ok()
+{
+    plugin_preset sp;
+    sp.name = gtk_entry_get_text(GTK_ENTRY(glade_xml_get_widget(store_preset_xml, "preset_name")));
+    sp.bank = 0;
+    sp.program = 0;
+    sp.plugin = effect_name;
+    int count = gui->plugin->get_param_count();
+    for (int i = 0; i < count; i++) {
+        sp.param_names.push_back(gui->plugin->get_param_names()[i]);
+        sp.values.push_back(gui->plugin->get_param_value(i));
+    }
+    presets.push_back(sp);
+    gtk_widget_destroy(store_preset_dlg);
+}
+
+void store_preset_cancel()
+{
+    gtk_widget_destroy(store_preset_dlg);
+}
+
+void store_preset()
+{
+    if (store_preset_dlg)
+    {
+        gtk_window_present(GTK_WINDOW(store_preset_dlg));
+        return;
+    }
+    store_preset_xml = glade_xml_new("calf.glade", NULL, NULL);
+    store_preset_dlg = glade_xml_get_widget(store_preset_xml, "store_preset");
+    gtk_signal_connect(GTK_OBJECT(store_preset_dlg), "destroy", G_CALLBACK(store_preset_dlg_destroy), NULL);
+    gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(store_preset_xml, "ok_button")), "clicked", G_CALLBACK(store_preset_ok), NULL);
+    gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(store_preset_xml, "cancel_button")), "clicked", G_CALLBACK(store_preset_cancel), NULL);
+    gtk_window_set_transient_for(GTK_WINDOW(store_preset_dlg), GTK_WINDOW(toplevel));
+}
+
+static const GtkActionEntry entries[] = {
+    { "HostMenuAction", "", "_Host", NULL, "Application-wide actions", NULL },
+    { "exit", "", "E_xit", NULL, "Exit the application", exit_gui },
+    { "PresetMenuAction", "", "_Preset", NULL, "Preset operations", NULL },
+    { "store-preset", "", "_Store preset", NULL, "Store a current setting as preset", store_preset },
+};
+
+void activate_preset(GtkAction *action, int preset)
+{
+    plugin_preset &p = presets[preset];
+    if (p.plugin != effect_name)
+        return;
+    map<string, int> names;
+    int count = gui->plugin->get_param_count();
+    for (int i = 0; i < count; i++) 
+        names[gui->plugin->get_param_names()[i]] = i;
+    // no support for unnamed parameters... tough luck :)
+    for (unsigned int i = 0; i < min(p.param_names.size(), p.values.size()); i++)
+    {
+        map<string, int>::iterator pos = names.find(p.param_names[i]);
+        if (pos == names.end())
+            continue;
+        gui->plugin->set_param_value(pos->second, p.values[i]);
+    }
+    gui->refresh();
+}
+
 void make_gui(jack_host_base *jh, const char *title, const char *effect)
 {
-    GtkWidget *toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-    gtk_window_set_title (GTK_WINDOW (toplevel), title);
+    toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    GtkVBox *vbox = GTK_VBOX(gtk_vbox_new(false, 5));
+    
+    gtk_window_set_title(GTK_WINDOW (toplevel), (string(title) + " - " + effect).c_str());
+    gtk_container_add(GTK_CONTAINER(toplevel), GTK_WIDGET(vbox));
+
+    GtkUIManager *ui_mgr = gtk_ui_manager_new();
+    GtkActionGroup *act_grp = gtk_action_group_new("default");
+    gtk_action_group_add_actions(act_grp, entries, sizeof(entries)/sizeof(entries[0]), jh);
+    GError *error = NULL;
+    gtk_ui_manager_insert_action_group(ui_mgr, act_grp, 0);
+    int merge_ui = gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);
+    
+    string preset_xml = preset_pre_xml;
+    for (unsigned int i = 0; i < presets.size(); i++)
+    {
+        if (presets[i].plugin != effect_name)
+            continue;
+        stringstream ss;
+        ss << "preset" << i;
+        preset_xml += "          <menuitem name=\""+presets[i].name+"\" action=\""+ss.str()+"\"/>\n";
+        
+        GtkActionEntry ae = { ss.str().c_str(), NULL, presets[i].name.c_str(), NULL, NULL, (GCallback)activate_preset };
+        gtk_action_group_add_actions(act_grp, &ae, 1, (gpointer)i);
         
+    }
+    preset_xml += preset_post_xml;
+    
+    gtk_ui_manager_add_ui_from_string(ui_mgr, preset_xml.c_str(), -1, &error);
+    GtkWidget *menu_bar = gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar");
+    
+    
+    gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(menu_bar));
+    
     gui = new plugin_gui(toplevel);
-    gui->create(jh, effect);
+    GtkWidget *table = gui->create(jh, effect);
 
-    gtk_signal_connect (GTK_OBJECT(toplevel), "destroy", G_CALLBACK(destroy), NULL);
+    gtk_container_add(GTK_CONTAINER(vbox), table);
+
+    gtk_signal_connect(GTK_OBJECT(toplevel), "destroy", G_CALLBACK(destroy), NULL);
     gtk_widget_show_all(toplevel);
     
 }
@@ -64,15 +357,10 @@ static struct option long_options[] = {
     {0,0,0,0},
 };
 
-const char *effect_name = "flanger";
-const char *client_name = "calfhost";
-const char *input_name = "input";
-const char *output_name = "output";
-const char *midi_name = "midi";
-
 int main(int argc, char *argv[])
 {
     gtk_init(&argc, &argv);
+    glade_init();
     while(1) {
         int option_index;
         int c = getopt_long(argc, argv, "c:e:i:o:m:p:hv", long_options, &option_index);
@@ -108,6 +396,7 @@ int main(int argc, char *argv[])
         }
     }
     try {
+        load_presets();
         jack_host_base *jh = NULL;
         if (!strcmp(effect_name, "reverb"))
             jh = new jack_host<reverb_audio_module>();
@@ -134,6 +423,7 @@ int main(int argc, char *argv[])
         gtk_main();
         delete gui;
         jh->close();
+        save_presets();
         delete jh;
     }
     catch(std::exception &e)

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list