[SCM] calf/master: + First implementation of XML-based GUI (for monosynth only so far)

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


The following commit has been merged in the master branch:
commit a0619044c5c694e4197d13a78c82e1caacf2f804
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Wed Dec 26 00:13:25 2007 +0000

    + First implementation of XML-based GUI (for monosynth only so far)
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@44 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/ChangeLog b/ChangeLog
index e69de29..f92fb5e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+Version 0.0.9
+
++ started creating an XML-based GUI (for monosynth only in this version)
diff --git a/src/calf/giface.h b/src/calf/giface.h
index 0c6694b..e618f06 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -102,6 +102,7 @@ struct plugin_ctl_iface
     virtual float get_param_value(int param_no) = 0;
     virtual void set_param_value(int param_no, float value) = 0;
     virtual int get_param_count() = 0;
+    virtual const char *get_gui_xml() = 0;
     virtual ~plugin_ctl_iface() {}
 };
 
@@ -128,6 +129,7 @@ public:
     uint32_t process_audio(uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask);
     static int get_in_channels();
     static int get_out_channels();
+    static const char *get_xml_iface() { return NULL; }
 };
 
 struct ladspa_info
@@ -172,6 +174,9 @@ struct ladspa_instance: public Module, public plugin_ctl_iface
     {
         return Module::param_count;
     }
+    virtual const char *get_gui_xml() {
+        return Module::get_gui_xml();
+    }
 };
 
 template<class Module>
@@ -437,7 +442,7 @@ struct ladspa_wrapper
     
     std::string generate_rdf() {
         return synth::generate_ladspa_rdf(info, Module::param_props, (const char **)descriptor.PortNames, Module::param_count, Module::in_count + Module::out_count);
-    };
+    }
 };
 
 template<class Module>
diff --git a/src/calf/gui.h b/src/calf/gui.h
index 78ebee3..c459956 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -22,6 +22,8 @@
 #ifndef __CALF_GUI_H
 #define __CALF_GUI_H
 
+#include <expat.h>
+#include <map>
 #include <vector>
 #include <gtk/gtk.h>
 #if USE_PHAT
@@ -32,16 +34,36 @@ namespace synth {
 
 class plugin_gui;
 
-struct param_control
+struct control_base
 {
+    typedef std::map<std::string, std::string> xml_attribute_map;
+    xml_attribute_map attribs;
     plugin_gui *gui;
+    void require_attribute(const char *name);
+    void require_int_attribute(const char *name);
+    int get_int(const char *name, int def_value = 0);
+    float get_float(const char *name, float def_value = 0.f);
+};
+
+#define _GUARD_CHANGE_ if (in_change) return; guard_change __gc__(this);
+
+struct param_control: public control_base
+{    
     int param_no;
     GtkWidget *label, *widget;
+    int in_change;
     
-    param_control() { gui = NULL; param_no = -1; label = NULL; }
+    struct guard_change {
+        param_control *pc;
+        guard_change(param_control *_pc) : pc(_pc) { pc->in_change++; }
+        ~guard_change() { pc->in_change--; }
+    };
+    
+    param_control() { gui = NULL; param_no = -1; label = NULL; in_change = 0;}
     inline parameter_properties &get_props();
     
-    virtual GtkWidget *create_label(int param_idx);
+    virtual void init_xml(const char *element) {}
+    virtual GtkWidget *create_label();
     virtual void update_label();
     /// called to create a widget for a control
     virtual GtkWidget *create(plugin_gui *_gui, int _param_no)=0;
@@ -49,9 +71,65 @@ struct param_control
     virtual void get()=0;
     /// called to transfer the value from parameter(s) to control
     virtual void set()=0;
+    virtual void hook_params();
     virtual ~param_control() {}
 };
 
+struct control_container: public control_base
+{
+    GtkContainer *container;
+    
+    virtual GtkWidget *create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes)=0;
+    virtual void add(GtkWidget *w, control_base *base) { gtk_container_add(container, w); }
+    virtual ~control_container() {}
+};
+
+struct table_container: public control_container
+{
+    virtual GtkWidget *create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes);
+    virtual void add(GtkWidget *w, control_base *base);
+};
+
+struct alignment_container: public control_container
+{
+    virtual GtkWidget *create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes);
+};
+
+struct frame_container: public control_container
+{
+    virtual GtkWidget *create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes);
+};
+
+struct box_container: public control_container
+{
+    virtual void add(GtkWidget *w, control_base *base);
+};
+
+struct vbox_container: public box_container
+{
+    virtual GtkWidget *create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes);
+};
+
+struct hbox_container: public box_container
+{
+    virtual GtkWidget *create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes);
+};
+
+struct label_param_control: public param_control
+{
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
+    virtual void get() {}
+    virtual void set() {}
+};
+
+struct value_param_control: public param_control
+{
+    virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
+    virtual void get() {}
+    virtual void set();
+};
+
+
 struct hscale_param_control: public param_control
 {
     GtkHScale *scale;
@@ -59,6 +137,7 @@ struct hscale_param_control: public param_control
     virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
     virtual void get();
     virtual void set();
+    virtual void init_xml(const char *element);
     static void hscale_value_changed(GtkHScale *widget, gpointer value);
     static gchar *hscale_format_value(GtkScale *widget, double arg1, gpointer value);
 };
@@ -101,17 +180,31 @@ class plugin_gui
 {
 protected:
     int param_count;
+    std::multimap<int, param_control *> par2ctl;
+    XML_Parser parser;
+    param_control *current_control;
+    std::vector<control_container *> container_stack;
+    control_container *top_container;
+    std::map<std::string, int> param_name_map;
 public:
     plugin_gui_window *window;
-    GtkWidget *table;
+    GtkWidget *container;
     const char *effect_name;
     plugin_ctl_iface *plugin;
     std::vector<param_control *> params;
 
     plugin_gui(plugin_gui_window *_window);
-    GtkWidget *create(plugin_ctl_iface *_plugin, const char *title);
+    GtkWidget *create(plugin_ctl_iface *_plugin);
+    GtkWidget *create_from_xml(plugin_ctl_iface *_plugin, const char *xml);
+    param_control *create_control_from_xml(const char *element, const char *attributes[]);
+    control_container *create_container_from_xml(const char *element, const char *attributes[]);
 
+    void add_param_ctl(int param, param_control *ctl) { par2ctl.insert(std::pair<int, param_control *>(param, ctl)); }
     void refresh();
+    void xml_element_start(const char *element, const char *attributes[]);
+    void set_param_value(int param_no, float value, param_control *originator = NULL);
+    static void xml_element_start(void *data, const char *element, const char *attributes[]);
+    static void xml_element_end(void *data, const char *element);
 
 #if USE_PHAT
     static void knob_value_changed(PhatKnob *widget, gpointer value);
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index e2abeef..b445b89 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -304,6 +304,9 @@ public:
         params[param_no] = value;
         changed = true;
     }
+    virtual const char *get_gui_xml() {
+        return Module::get_gui_xml();
+    }
 };
 
 extern jack_host_base *create_jack_host(const char *name);
diff --git a/src/calf/modules.h b/src/calf/modules.h
index 409f50c..3d3ae69 100644
--- a/src/calf/modules.h
+++ b/src/calf/modules.h
@@ -42,6 +42,7 @@ public:
     inline void activate() {}
     inline void deactivate() {}
     inline void set_sample_rate(uint32_t sr) { }
+    inline static const char *get_gui_xml() { return NULL; }
 };
 
 class amp_audio_module: public null_audio_module
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index 329fa35..44e58e7 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -64,6 +64,7 @@ public:
     synth::adsr envelope;
     
     static parameter_properties param_props[];
+    static const char *get_gui_xml();
     void set_sample_rate(uint32_t sr);
     void delayed_note_on();
     void note_on(int note, int vel)
diff --git a/src/gui.cpp b/src/gui.cpp
index 472fa88..90457c4 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -30,7 +30,7 @@ using namespace std;
 
 /******************************** controls ********************************/
 
-GtkWidget *param_control::create_label(int param_idx)
+GtkWidget *param_control::create_label()
 {
     label = gtk_label_new ("");
     gtk_label_set_width_chars (GTK_LABEL (label), 12);
@@ -44,6 +44,12 @@ void param_control::update_label()
     gtk_label_set_text (GTK_LABEL (label), props.to_string(gui->plugin->get_param_value(param_no)).c_str());
 }
 
+void param_control::hook_params()
+{
+    if (param_no != -1)
+        gui->add_param_ctl(param_no, this);
+}
+
 // combo box
 
 GtkWidget *combo_box_param_control::create(plugin_gui *_gui, int _param_no)
@@ -62,6 +68,7 @@ GtkWidget *combo_box_param_control::create(plugin_gui *_gui, int _param_no)
 
 void combo_box_param_control::set()
 {
+    _GUARD_CHANGE_
     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);
 }
@@ -69,7 +76,7 @@ void combo_box_param_control::set()
 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);
+    gui->set_param_value(param_no, gtk_combo_box_get_active (GTK_COMBO_BOX(widget)) + props.min, this);
 }
 
 void combo_box_param_control::combo_value_changed(GtkComboBox *widget, gpointer value)
@@ -93,18 +100,31 @@ GtkWidget *hscale_param_control::create(plugin_gui *_gui, int _param_no)
     return widget;
 }
 
+void hscale_param_control::init_xml(const char *element)
+{
+    if (attribs.count("width"))
+        gtk_widget_set_size_request (widget, get_int("width", 200), -1);
+    if (attribs.count("position"))
+    {
+        string v = attribs["position"];
+        if (v == "top") gtk_scale_set_value_pos(GTK_SCALE(widget), GTK_POS_TOP);
+        if (v == "bottom") gtk_scale_set_value_pos(GTK_SCALE(widget), GTK_POS_BOTTOM);
+    }
+}
+
 void hscale_param_control::set()
 {
+    _GUARD_CHANGE_
     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);
+    // 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)));
-    gui->plugin->set_param_value(param_no, cvalue);
+    gui->set_param_value(param_no, cvalue, this);
 }
 
 void hscale_param_control::hscale_value_changed(GtkHScale *widget, gpointer value)
@@ -124,6 +144,36 @@ gchar *hscale_param_control::hscale_format_value(GtkScale *widget, double arg1,
     return g_strdup (props.to_string (cvalue).c_str());
 }
 
+// label
+
+GtkWidget *label_param_control::create(plugin_gui *_gui, int _param_no)
+{
+    gui = _gui, param_no = _param_no;
+    string text = "<no name>";
+    if (param_no != -1)
+        text = get_props().name;
+    widget = gtk_label_new(text.c_str());
+    return widget;
+}
+
+// value
+
+GtkWidget *value_param_control::create(plugin_gui *_gui, int _param_no)
+{
+    gui = _gui, param_no = _param_no;
+    widget = gtk_label_new ("");
+    gtk_label_set_width_chars (GTK_LABEL (widget), 12);
+    gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.5);
+    return widget;
+}
+
+void value_param_control::set()
+{
+    _GUARD_CHANGE_
+    parameter_properties &props = get_props();
+    gtk_label_set_text (GTK_LABEL (widget), props.to_string(gui->plugin->get_param_value(param_no)).c_str());    
+}
+
 // check box
 
 GtkWidget *toggle_param_control::create(plugin_gui *_gui, int _param_no)
@@ -145,12 +195,12 @@ void toggle_param_control::toggle_value_changed(GtkCheckButton *widget, gpointer
 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);
+    gui->set_param_value(param_no, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widget)) + props.min, this);
 }
 
 void toggle_param_control::set()
 {
+    _GUARD_CHANGE_
     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);
 }
@@ -173,11 +223,14 @@ 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);
+    gui->set_param_value(param_no, value, this);
+    if (label)
+        update_label();
 }
 
 void knob_param_control::set()
 {
+    _GUARD_CHANGE_
     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);
@@ -186,11 +239,7 @@ void knob_param_control::set()
 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 = jhp->get_props();
-    float cvalue = props.from_01 (phat_knob_get_value (widget));
-    pif.set_param_value(jhp->param_no, cvalue);
-    jhp->update_label();
+    jhp->get();
 }
 #endif
 
@@ -207,7 +256,7 @@ static void action_destroy_notify(gpointer data)
     delete (activate_preset_params *)data;
 }
 
-GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
+GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin)
 {
     plugin = _plugin;
     param_count = plugin->get_param_count();
@@ -216,14 +265,14 @@ GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
         params[i] = NULL;
     }
     
-    table = gtk_table_new (param_count, 3, FALSE);
+    container = gtk_table_new (param_count, 3, FALSE);
     
     for (int i = 0; i < param_count; i++) {
         int trow = i;
         parameter_properties &props = *plugin->get_param_props(i);
         GtkWidget *label = gtk_label_new (props.name);
         gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
-        gtk_table_attach (GTK_TABLE (table), label, 0, 1, trow, trow + 1, GTK_FILL, GTK_FILL, 2, 2);
+        gtk_table_attach (GTK_TABLE (container), label, 0, 1, trow, trow + 1, GTK_FILL, GTK_FILL, 2, 2);
         
         
         GtkWidget *widget = NULL;
@@ -233,22 +282,22 @@ GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
         {
             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);
+            gtk_table_attach (GTK_TABLE (container), 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)
         {
             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);
+            gtk_table_attach (GTK_TABLE (container), widget, 1, 3, trow, trow + 1, GTK_EXPAND, GTK_SHRINK, 0, 0);
         }
 #if USE_PHAT
         else if ((props.flags & PF_CTLMASK) != PF_CTL_FADER)
         {
             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);
+            gtk_table_attach (GTK_TABLE (container), widget, 1, 2, trow, trow + 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+            gtk_table_attach (GTK_TABLE (container), params[i]->create_label(), 2, 3, trow, trow + 1, (GtkAttachOptions)(GTK_SHRINK | GTK_FILL), GTK_SHRINK, 0, 0);
         }
 #endif
         else
@@ -256,13 +305,248 @@ GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
             gtk_misc_set_alignment (GTK_MISC (label), 1.0, 1.0);
             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);
+            gtk_table_attach (GTK_TABLE (container), widget, 1, 3, trow, trow + 1, (GtkAttachOptions)(GTK_EXPAND | GTK_FILL), GTK_SHRINK, 0, 0);
         }
         params[i]->set();
     }
+    return container;
+}
+
+void control_base::require_attribute(const char *name)
+{
+    if (attribs.count(name) == 0) {
+        g_error("Missing attribute: %s", name);
+    }
+}
+
+void control_base::require_int_attribute(const char *name)
+{
+    if (attribs.count(name) == 0) {
+        g_error("Missing attribute: %s", name);
+    }
+    if (attribs[name].empty() || attribs[name].find_first_not_of("0123456789") != string::npos) {
+        g_error("Wrong data type on attribute: %s (required integer)", name);
+    }
+}
+
+int control_base::get_int(const char *name, int def_value)
+{
+    if (attribs.count(name) == 0)
+        return def_value;
+    const std::string &v = attribs[name];
+    if (v.empty() || v.find_first_not_of("-+0123456789") != string::npos)
+        return def_value;
+    return atoi(v.c_str());
+}
+
+float control_base::get_float(const char *name, float def_value)
+{
+    if (attribs.count(name) == 0)
+        return def_value;
+    const std::string &v = attribs[name];
+    if (v.empty() || v.find_first_not_of("-+0123456789.") != string::npos)
+        return def_value;
+    stringstream ss(v);
+    float value;
+    ss >> value;
+    return value;
+}
+
+/******************************** GtkTable container ********************************/
+
+GtkWidget *table_container::create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes)
+{
+    require_int_attribute("rows");
+    require_int_attribute("cols");
+    GtkWidget *table = gtk_table_new(get_int("rows", 1), get_int("cols", 1), false);
+    container = GTK_CONTAINER(table);
     return table;
 }
 
+void table_container::add(GtkWidget *widget, control_base *base)
+{
+    base->require_int_attribute("attach-x");
+    base->require_int_attribute("attach-y");
+    int x = base->get_int("attach-x"), y = base->get_int("attach-y");
+    int w = base->get_int("attach-w", 1), h = base->get_int("attach-h", 1);
+    int fillx = (base->get_int("fill-x", 1) ? GTK_FILL : 0) | (base->get_int("expand-x", 1) ? GTK_EXPAND : 0) | (base->get_int("shrink-x", 0) ? GTK_SHRINK : 0);
+    int filly = (base->get_int("fill-y", 1) ? GTK_FILL : 0) | (base->get_int("expand-y", 1) ? GTK_EXPAND : 0) | (base->get_int("shrink-y", 0) ? GTK_SHRINK : 0);
+    int padx = base->get_int("pad-x", 2);
+    int pady = base->get_int("pad-y", 2);
+    gtk_table_attach(GTK_TABLE(container), widget, x, x + w, y, y + h, (GtkAttachOptions)fillx, (GtkAttachOptions)filly, padx, pady);
+} 
+
+/******************************** alignment contaner ********************************/
+
+GtkWidget *alignment_container::create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes)
+{
+    GtkWidget *align = gtk_alignment_new(get_float("align-x", 0.5), get_float("align-y", 0.5), get_float("scale-x", 0), get_float("scale-y", 0));
+    container = GTK_CONTAINER(align);
+    return align;
+}
+
+/******************************** GtkFrame contaner ********************************/
+
+GtkWidget *frame_container::create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes)
+{
+    GtkWidget *frame = gtk_frame_new(attribs["label"].c_str());
+    container = GTK_CONTAINER(frame);
+    return frame;
+}
+
+/******************************** GtkBox type of containers ********************************/
+
+void box_container::add(GtkWidget *w, control_base *base)
+{
+    gtk_container_add_with_properties(container, w, "expand", get_int("expand", 1), "fill", get_int("fill", 1), NULL);
+}
+
+/******************************** GtkHBox container ********************************/
+
+GtkWidget *hbox_container::create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes)
+{
+    GtkWidget *hbox = gtk_hbox_new(false, get_int("spacing", 2));
+    container = GTK_CONTAINER(hbox);
+    return hbox;
+}
+
+/******************************** GtkVBox container ********************************/
+
+GtkWidget *vbox_container::create(plugin_gui *_gui, const char *element, xml_attribute_map &attributes)
+{
+    GtkWidget *vbox = gtk_vbox_new(false, get_int("spacing", 2));
+    container = GTK_CONTAINER(vbox);
+    return vbox;
+}
+
+/******************************** GUI proper ********************************/
+
+param_control *plugin_gui::create_control_from_xml(const char *element, const char *attributes[])
+{
+    if (!strcmp(element, "knob"))
+        return new knob_param_control;
+    if (!strcmp(element, "hscale"))
+        return new hscale_param_control;
+    if (!strcmp(element, "combo"))
+        return new combo_box_param_control;
+    if (!strcmp(element, "toggle"))
+        return new toggle_param_control;
+    if (!strcmp(element, "label"))
+        return new label_param_control;
+    if (!strcmp(element, "value"))
+        return new value_param_control;
+    return NULL;
+}
+
+control_container *plugin_gui::create_container_from_xml(const char *element, const char *attributes[])
+{
+    if (!strcmp(element, "table"))
+        return new table_container;
+    if (!strcmp(element, "vbox"))
+        return new vbox_container;
+    if (!strcmp(element, "hbox"))
+        return new hbox_container;
+    if (!strcmp(element, "align"))
+        return new alignment_container;
+    if (!strcmp(element, "frame"))
+        return new frame_container;
+    return NULL;
+}
+
+void plugin_gui::xml_element_start(void *data, const char *element, const char *attributes[])
+{
+    plugin_gui *gui = (plugin_gui *)data;
+    gui->xml_element_start(element, attributes);
+}
+
+void plugin_gui::xml_element_start(const char *element, const char *attributes[])
+{
+    control_base::xml_attribute_map xam;
+    while(*attributes)
+    {
+        xam[attributes[0]] = attributes[1];
+        attributes += 2;
+    }
+    
+    control_container *cc = create_container_from_xml(element, attributes);
+    if (cc != NULL)
+    {
+        cc->attribs = xam;
+        cc->create(this, element, xam);
+        gtk_container_set_border_width(cc->container, cc->get_int("border"));
+
+        container_stack.push_back(cc);
+        current_control = NULL;
+        return;
+    }
+    if (!container_stack.empty())
+    {
+        current_control = create_control_from_xml(element, attributes);
+        if (current_control)
+        {
+            current_control->attribs = xam;
+            int param_no = -1;
+            if (xam.count("param"))
+            {
+                map<string, int>::iterator it = param_name_map.find(xam["param"]);
+                if (it == param_name_map.end())
+                    g_error("Unknown parameter %s", xam["param"].c_str());
+                else
+                    param_no = it->second;
+            }
+            current_control->create(this, param_no);
+            current_control->init_xml(element);
+            current_control->set();
+            current_control->hook_params();
+            params.push_back(current_control);
+            return;
+        }
+    }
+    g_error("Unxpected element %s in GUI definition\n", element);
+}
+
+void plugin_gui::xml_element_end(void *data, const char *element)
+{
+    plugin_gui *gui = (plugin_gui *)data;
+    if (gui->current_control)
+    {
+        (*gui->container_stack.rbegin())->add(gui->current_control->widget, gui->current_control);
+        gui->current_control = NULL;
+        return;
+    }
+    unsigned int ss = gui->container_stack.size();
+    if (ss > 1) {
+        gui->container_stack[ss - 2]->add(GTK_WIDGET(gui->container_stack[ss - 1]->container), gui->container_stack[ss - 1]);
+    }
+    else
+        gui->top_container = gui->container_stack[0];
+    gui->container_stack.pop_back();
+}
+
+
+GtkWidget *plugin_gui::create_from_xml(plugin_ctl_iface *_plugin, const char *xml)
+{
+    current_control = NULL;
+    top_container = NULL;
+    parser = XML_ParserCreate("UTF-8");
+    plugin = _plugin;
+    container_stack.clear();
+    
+    param_name_map.clear();
+    int size = plugin->get_param_count();
+    for (int i = 0; i < size; i++)
+        param_name_map[plugin->get_param_props(i)->short_name] = i;
+    
+    XML_SetUserData(parser, this);
+    XML_SetElementHandler(parser, xml_element_start, xml_element_end);
+    XML_Status status = XML_Parse(parser, xml, strlen(xml), 1);
+    if (status == XML_STATUS_ERROR)
+        g_error("Parse error: %s in XML", XML_ErrorString(XML_GetErrorCode(parser)));
+    
+    XML_ParserFree(parser);
+    return GTK_WIDGET(top_container->container);
+}
+
 void plugin_gui::refresh()
 {
     for (unsigned int i = 0; i < params.size(); i++)
@@ -272,6 +556,19 @@ void plugin_gui::refresh()
     }
 }
 
+void plugin_gui::set_param_value(int param_no, float value, param_control *originator)
+{
+    plugin->set_param_value(param_no, value);
+    std::multimap<int, param_control *>::iterator it = par2ctl.find(param_no);
+    while(it != par2ctl.end() && it->first == param_no)
+    {
+        if (it->second != originator)
+            it->second->set();
+        it++;
+    }
+}
+
+
 /******************************* Actions **************************************************/
  
 static void store_preset_action(GtkAction *action, plugin_gui_window *gui_win)
@@ -389,16 +686,21 @@ void plugin_gui_window::create(plugin_ctl_iface *_jh, const char *title, const c
     gtk_widget_size_request(GTK_WIDGET(toplevel), &req2);
     // printf("size request %dx%d\n", req2.width, req2.height);
     
-    GtkWidget *table = gui->create(_jh, effect);
+    GtkWidget *container;
+    const char *xml = _jh->get_gui_xml();
+    if (xml)
+        container = gui->create_from_xml(_jh, xml);
+    else
+        container = gui->create(_jh);
     GtkWidget *sw = gtk_scrolled_window_new(NULL, NULL);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_NONE);
-    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), table);
+    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), container);
     
     gtk_box_pack_start(GTK_BOX(vbox), sw, true, true, 0);
     
     gtk_widget_show_all(GTK_WIDGET(sw));
-    gtk_widget_size_request(table, &req);
+    gtk_widget_size_request(container, &req);
     // printf("size request %dx%d\n", req.width, req.height);
     gtk_window_resize(GTK_WINDOW(toplevel), max(req.width + 10, req2.width), req.height + req2.height + 10);
     // gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(sw), GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, req.height, 20, 100, 100)));
diff --git a/src/monosynth.cpp b/src/monosynth.cpp
index d6024a3..a20809c 100644
--- a/src/monosynth.cpp
+++ b/src/monosynth.cpp
@@ -49,6 +49,129 @@ const char *monosynth_filter_choices[] = {
     "2x6dB/oct Bandpass",
 };
 
+static const char *monosynth_gui_xml =
+    "<vbox border=\"10\">"
+        "<hbox spacing=\"10\">"
+            "<frame label=\"Oscillators\">"
+                "<vbox border=\"10\">"
+                    "<table rows=\"2\" cols=\"2\">"
+                    "<label attach-x=\"0\" attach-y=\"0\" param=\"o1_wave\" />"
+                    "<label attach-x=\"1\" attach-y=\"0\" param=\"o2_wave\" />"
+                    "<combo attach-x=\"0\" attach-y=\"1\" param=\"o1_wave\" />"
+                    "<combo attach-x=\"1\" attach-y=\"1\" param=\"o2_wave\" />"
+                    "</table>"
+                    "<label param=\"o12_mix\"/>"
+                    "<hscale param=\"o12_mix\" position=\"bottom\"/>"
+                    "<hbox>"
+                        "<vbox>"
+                        "  <label param=\"o12_detune\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"o12_detune\" expand=\"0\" fill=\"0\"/></align><value param=\"o12_detune\"/>"
+                        "</vbox>"
+                        "<align align-x=\"0.5\" align-y=\"0.5\">"
+                        "  <vbox>"
+                        "    <label param=\"phase_mode\" />"
+                        "    <combo param=\"phase_mode\"/>"
+                        "  </vbox>"
+                        "</align>" 
+                        "<vbox>"
+                        "  <label param=\"o2_xpose\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"o2_xpose\" expand=\"0\" fill=\"0\"/></align><value param=\"o2_xpose\"/>"
+                        "</vbox>"
+                    "</hbox>"
+                "</vbox>"
+            "</frame>"
+            "<frame label=\"Filter\">"
+                "<vbox border=\"10\">"
+                    "<align align-x=\"0.5\" align-y=\"0.5\">"
+                        "<hbox>"
+                            "<label param=\"filter\" /><combo param=\"filter\" />"
+                        "</hbox>"
+                    "</align>"
+                    "<hbox>"
+                        "<vbox>"
+                        "  <label param=\"cutoff\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"cutoff\" expand=\"0\" fill=\"0\"/></align><value param=\"cutoff\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"res\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"res\" expand=\"0\" fill=\"0\"/></align><value param=\"res\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"filter_sep\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"filter_sep\" expand=\"0\" fill=\"0\"/></align><value param=\"filter_sep\"/>"
+                        "</vbox>"
+                    "</hbox>"
+                "</vbox>"
+            "</frame>"
+        "</hbox>"
+        "<hbox spacing=\"10\">"
+            "<frame label=\"Envelope\">"
+                "<vbox border=\"10\" spacing=\"10\">"
+                    "<hbox>"
+                        "<vbox>"
+                        "  <label param=\"adsr_a\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"adsr_a\" expand=\"0\" fill=\"0\"/></align><value param=\"adsr_a\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"adsr_d\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"adsr_d\" expand=\"0\" fill=\"0\"/></align><value param=\"adsr_d\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"adsr_s\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"adsr_s\" expand=\"0\" fill=\"0\"/></align><value param=\"adsr_s\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"adsr_r\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"adsr_r\" expand=\"0\" fill=\"0\"/></align><value param=\"adsr_r\"/>"
+                        "</vbox>"
+                    "</hbox>"
+                    "<hbox>"
+                        "<vbox>"
+                        "  <label param=\"env2cutoff\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"env2cutoff\" expand=\"0\" fill=\"0\"/></align><value param=\"env2cutoff\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"env2res\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"env2res\" expand=\"0\" fill=\"0\"/></align><value param=\"env2res\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"env2amp\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"env2amp\" expand=\"0\" fill=\"0\"/></align><value param=\"env2amp\"/>"
+                        "</vbox>"
+                    "</hbox>"
+                "</vbox>"
+            "</frame>"
+            "<frame label=\"Settings\">"
+                "<vbox border=\"10\" spacing=\"10\">"
+                    "<hbox>"
+                        "<vbox>"
+                            "<label param=\"key_follow\" />"
+                            "<align align-x=\"0.5\" align-y=\"0.5\"><toggle param=\"key_follow\" /></align>"
+                        "</vbox>"
+                        "<vbox>"
+                            "<label param=\"legato\"  expand=\"0\"/>"
+                            "<combo param=\"legato\" expand=\"0\" fill=\"0\"/>"
+                        "</vbox>"
+                    "</hbox>"
+                    "<hbox>"
+                        "<vbox>"
+                        "  <label param=\"portamento\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"portamento\" /></align><value param=\"portamento\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"vel2filter\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"vel2filter\" /></align><value param=\"vel2filter\"/>"
+                        "</vbox>"
+                        "<vbox>"
+                        "  <label param=\"vel2amp\" />"
+                        "  <align align-x=\"0.5\" align-y=\"0.5\"><knob param=\"vel2amp\" /></align><value param=\"vel2amp\"/>"
+                        "</vbox>"
+                    "</hbox>"
+                "</vbox>"
+            "</frame>"
+        "</hbox>"
+    "</vbox>";
+
 parameter_properties monosynth_audio_module::param_props[] = {
     { wave_saw,         0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o1_wave", "Osc1 Wave" },
     { wave_sqr,         0, wave_count - 1, 1, PF_ENUM | PF_CTL_COMBO, monosynth_waveform_names, "o2_wave", "Osc2 Wave" },
@@ -77,6 +200,11 @@ parameter_properties monosynth_audio_module::param_props[] = {
     { 0,          0,    1,  0.1, PF_FLOAT | PF_SCALE_PERC | PF_CTL_KNOB, NULL, "vel2amp", "Vel->Amp" },
 };
 
+const char *monosynth_audio_module::get_gui_xml()
+{
+    return monosynth_gui_xml;
+}
+
 void monosynth_audio_module::activate() {
     running = false;
     output_pos = 0;
diff --git a/src/preset.cpp b/src/preset.cpp
index 3eeb605..440d7b8 100644
--- a/src/preset.cpp
+++ b/src/preset.cpp
@@ -212,6 +212,7 @@ void preset_list::load(const char *filename)
     if (status == XML_STATUS_ERROR)
         throw preset_exception(string("Parse error: ") + XML_ErrorString(XML_GetErrorCode(parser))+ " in ", filename, errno);
     close(fd);
+    XML_ParserFree(parser);
 }
 
 void preset_list::save(const char *filename)

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list