[SCM] calf/master: + GUI, JACK host: implemented primitive main window + VU Meter: added, used in JACK host + at least some LV2 and DSSI GUI support is temporarily broken

js at users.alioth.debian.org js at users.alioth.debian.org
Tue May 7 15:37:04 UTC 2013


The following commit has been merged in the master branch:
commit 486dd24340ba56270287b9609fb2d8f16fd37131
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Tue Feb 12 00:04:52 2008 +0000

    + GUI, JACK host: implemented primitive main window
    + VU Meter: added, used in JACK host
    + at least some LV2 and DSSI GUI support is temporarily broken
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@127 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/src/Makefile.am b/src/Makefile.am
index e7b7c8c..5999aca 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -48,7 +48,7 @@ calf_la_SOURCES = modules.cpp giface.cpp monosynth.cpp organ.cpp preset.cpp synt
 calf_la_LDFLAGS = -rpath $(ladspadir) -avoid-version -module -lexpat -export-symbols-regex "(ladspa_|lv2_|dssi_)descriptor"
 
 if USE_LV2_GUI
-calflv2gui_la_SOURCES = gui.cpp custom_ctl.cpp modules.cpp giface.cpp monosynth.cpp organ.cpp preset.cpp synth.cpp lv2gui.cpp
+calflv2gui_la_SOURCES = gui.cpp custom_ctl.cpp modules.cpp giface.cpp monosynth.cpp organ.cpp preset.cpp synth.cpp lv2gui.cpp main_win.cpp
 calflv2gui_la_LDFLAGS = -rpath $(lv2dir) -avoid-version -module -lexpat -export-symbols-regex "lv2_gui_descriptor" $(GUI_DEPS_LIBS)
 endif
 
@@ -56,7 +56,7 @@ libcalfstatic_la_SOURCES = modules.cpp giface.cpp monosynth.cpp organ.cpp preset
 libcalfstatic_la_LDFLAGS = -static -lexpat
 
 if USE_GUI
-libcalfgui_la_SOURCES = gui.cpp preset_gui.cpp custom_ctl.cpp osctl.cpp
+libcalfgui_la_SOURCES = gui.cpp preset_gui.cpp custom_ctl.cpp osctl.cpp main_win.cpp
 libcalfgui_la_LDFLAGS = -static
 endif
 
diff --git a/src/calf/custom_ctl.h b/src/calf/custom_ctl.h
index de9ab1d..372a9be 100644
--- a/src/calf/custom_ctl.h
+++ b/src/calf/custom_ctl.h
@@ -28,6 +28,28 @@ extern GtkWidget *calf_line_graph_new();
 
 extern GType calf_line_graph_get_type();
 
+#define CALF_TYPE_VUMETER          (calf_vumeter_get_type())
+#define CALF_VUMETER(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALF_TYPE_VUMETER, CalfVUMeter))
+#define CALF_IS_VUMETER(obj)       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALF_TYPE_VUMETER))
+#define CALF_VUMETER_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass),  CALF_TYPE_VUMETER, CalfVUMeterClass))
+#define CALF_IS_VUMETER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((klass),  CALF_TYPE_VUMETER))
+
+struct CalfVUMeter
+{
+    GtkWidget parent;
+    float value;
+};
+
+struct CalfVUMeterClass
+{
+    GtkWidgetClass parent_class;
+};
+
+extern GtkWidget *calf_vumeter_new();
+extern GType calf_vumeter_get_type();
+extern void calf_vumeter_set_value(CalfVUMeter *meter, float value);
+extern float calf_vumeter_get_value(CalfVUMeter *meter);
+
 #define CALF_TYPE_KNOB          (calf_knob_get_type())
 #define CALF_KNOB(obj)          (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALF_TYPE_KNOB, CalfKnob))
 #define CALF_IS_KNOB(obj)       (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALF_TYPE_KNOB))
diff --git a/src/calf/giface.h b/src/calf/giface.h
index bf89dd3..941c4a2 100644
--- a/src/calf/giface.h
+++ b/src/calf/giface.h
@@ -118,6 +118,12 @@ struct plugin_ctl_iface
     virtual line_graph_iface *get_line_graph_iface() = 0;
     virtual int get_param_port_offset() = 0;
     virtual bool activate_preset(int bank, int program) = 0;
+    virtual const char *get_name() = 0;
+    virtual const char *get_id() = 0;
+    virtual int get_input_count()=0;
+    virtual int get_output_count()=0;
+    virtual bool get_midi()=0;
+    virtual float get_level(int port)=0;
     virtual ~plugin_ctl_iface() {}
 };
 
@@ -194,6 +200,18 @@ struct ladspa_instance: public Module, public plugin_ctl_iface
     virtual bool activate_preset(int bank, int program) { 
         return false;
     }
+    virtual const char *get_name()
+    {
+        return Module::get_name();
+    }
+    virtual const char *get_id()
+    {
+        return Module::get_id();
+    }
+    virtual int get_input_count() { return Module::in_count; }
+    virtual int get_output_count() { return Module::out_count; }
+    virtual bool get_midi() { return Module::support_midi; }
+    virtual float get_level(int port) { return 0.f; }
 };
 
 template<class Module>
diff --git a/src/calf/gui.h b/src/calf/gui.h
index b598fa8..fc0016e 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -69,7 +69,8 @@ struct param_control: public control_base
     /// called to transfer the value from parameter(s) to control
     virtual void set()=0;
     virtual void hook_params();
-    virtual ~param_control() {}
+    virtual void on_idle() {}
+    virtual ~param_control();
 };
 
 struct control_container: public control_base
@@ -172,13 +173,12 @@ struct combo_box_param_control: public param_control
 struct line_graph_param_control: public param_control
 {
     CalfLineGraph *graph;
-    int source_id;
 
     virtual GtkWidget *create(plugin_gui *_gui, int _param_no);
     virtual void get() {}
     virtual void set();
+    virtual void on_idle();
     virtual ~line_graph_param_control();
-    static gboolean update(void *data);
 };
 
 struct knob_param_control: public param_control
@@ -222,10 +222,24 @@ public:
     void refresh(int param_no, param_control *originator = NULL);
     void xml_element_start(const char *element, const char *attributes[]);
     void set_param_value(int param_no, float value, param_control *originator = NULL);
+    void on_idle();
+    ~plugin_gui();
     static void xml_element_start(void *data, const char *element, const char *attributes[]);
     static void xml_element_end(void *data, const char *element);
 };
 
+class main_window_iface
+{
+public:
+    virtual void add_plugin(plugin_ctl_iface *plugin)=0;
+    virtual void del_plugin(plugin_ctl_iface *plugin)=0;
+    
+    virtual void set_window(plugin_ctl_iface *plugin, plugin_gui_window *window)=0;
+    virtual void refresh_all_presets()=0;
+    virtual bool check_condition(const char *name)=0;
+    virtual ~main_window_iface() {}
+};
+
 class plugin_gui_window
 {
 public:
@@ -233,14 +247,15 @@ public:
     GtkWindow *toplevel;
     GtkUIManager *ui_mgr;
     GtkActionGroup *std_actions, *preset_actions;
-    std::set<std::string> conditions;
-    static std::set<plugin_gui_window *> all_windows;
+    main_window_iface *main;
+    int source_id;
 
-    plugin_gui_window();
+    plugin_gui_window(main_window_iface *_main);
     std::string make_gui_preset_list(GtkActionGroup *grp);
     void fill_gui_presets();
     void create(plugin_ctl_iface *_plugin, const char *title, const char *effect);
-    static void refresh_all_presets();
+    void close();
+    static gboolean on_idle(void *data);
     ~plugin_gui_window();
 };
 
diff --git a/src/calf/jackhost.h b/src/calf/jackhost.h
index f0d44fb..2b56bd9 100644
--- a/src/calf/jackhost.h
+++ b/src/calf/jackhost.h
@@ -1,10 +1,6 @@
 /* Calf DSP Library Utility Application - calfjackhost
  * API wrapper for JACK Audio Connection Kit
  *
- * Note: while this header file is licensed under LGPL license,
- * the application as a whole is GPLed because of partial dependency
- * on phat graphics library.
- *
  * Copyright (C) 2007 Krzysztof Foltman
  *
  * This program is free software; you can redistribute it and/or
@@ -111,15 +107,12 @@ public:
     bool changed;
     port midi_port;
     std::string name;
-    virtual int get_input_count()=0;
-    virtual int get_output_count()=0;
     virtual port *get_inputs()=0;
     virtual port *get_outputs()=0;
     virtual port *get_midi_port() { return get_midi() ? &midi_port : NULL; }
     virtual float *get_params()=0;
     virtual void init_module()=0;
     virtual void cache_ports()=0;
-    virtual bool get_midi()=0;
     virtual int process(jack_nframes_t nframes)=0;
     
     jack_host_base() {
@@ -194,12 +187,38 @@ public:
     }
 };
 
+struct vumeter
+{
+    float level, falloff;
+    
+    vumeter()
+    {
+        falloff = 0.999f;
+    }
+    
+    inline void update(float *src, unsigned int len)
+    {
+        double tmp = level;
+        for (unsigned int i = 0; i < len; i++)
+            tmp = std::max(tmp * falloff, (double)fabs(src[i]));
+        level = tmp;
+        dsp::sanitize(level);
+    }
+    inline void update_zeros(unsigned int len)
+    {
+        level *= pow((double)falloff, (double)len);
+        dsp::sanitize(level);
+    }
+};
+
 template<class Module>
 class jack_host: public jack_host_base {
 public:
     Module module;
     port inputs[Module::in_count], outputs[Module::out_count];
+    vumeter input_vus[Module::in_count], output_vus[Module::out_count];
     float params[Module::param_count];
+    float midi_meter;
     
     jack_host()
     {
@@ -207,6 +226,7 @@ public:
             module.params[i] = &params[i];
             params[i] = Module::param_props[i].def_value;
         }
+        midi_meter = 0;
     }
     
     virtual void init_module() {
@@ -240,6 +260,38 @@ public:
             break;
         }
     }
+    void process_part(unsigned int time, unsigned int len)
+    {
+        if (!len)
+            return;
+        for (int i = 0; i < Module::in_count; i++)
+            input_vus[i].update(module.ins[i] + time, len);
+        unsigned int mask = module.process(time, len, -1, -1);
+        for (int i = 0; i < Module::out_count; i++)
+        {
+            if (!(mask & (1 << i))) {
+                dsp::zero(module.outs[i] + time, len);
+                output_vus[i].update_zeros(len);
+            } else
+                output_vus[i].update(module.outs[i] + time, len);
+        }
+        // decay linearly for 0.1s
+        float new_meter = midi_meter - len / (0.1 * client->sample_rate);
+        if (new_meter < 0)
+            new_meter = 0;
+        midi_meter = new_meter;
+    }
+    virtual float get_level(int port) { 
+        if (port < Module::in_count)
+            return input_vus[port].level;
+        port -= Module::in_count;
+        if (port < Module::out_count)
+            return output_vus[port].level;
+        port -= Module::out_count;
+        if (port == 0 && Module::support_midi)
+            return midi_meter;
+        return 0.f;
+    }
     int process(jack_nframes_t nframes)
     {
         for (int i=0; i<Module::in_count; i++) {
@@ -253,7 +305,6 @@ public:
         }
 
         unsigned int time = 0;
-        unsigned int mask = 0;
         if (Module::support_midi)
         {
             jack_midi_event_t event;
@@ -269,23 +320,16 @@ public:
 #else
                 jack_midi_event_get(&event, midi_port.data, i);
 #endif
-                mask = module.process(time, event.time - time, -1, -1);
-                for (int i = 0; i < Module::out_count; i++) {
-                    if (!(mask & (1 << i)))
-                        dsp::zero(module.outs[i] + time, event.time - time);
-                }
+                unsigned int len = event.time - time;
+                process_part(time, len);
                 
+                midi_meter = 1.f;
                 handle_event(event.buffer, event.size);
                 
                 time = event.time;
             }
         }
-        mask = module.process(time, nframes - time, -1, -1);
-        for (int i = 0; i < Module::out_count; i++) {
-            // zero unfilled outputs
-            if (!(mask & (1 << i)))
-                dsp::zero(module.outs[i] + time, nframes - time);
-        }
+        process_part(time, nframes - time);
         return 0;
     }
     
@@ -327,6 +371,14 @@ public:
     {
         return &module;
     }
+    virtual const char *get_name()
+    {
+        return Module::get_name();
+    }
+    virtual const char *get_id()
+    {
+        return Module::get_id();
+    }
 };
 
 extern jack_host_base *create_jack_host(const char *name);
diff --git a/src/calf/lv2wrap.h b/src/calf/lv2wrap.h
index de9948f..d0552d8 100644
--- a/src/calf/lv2wrap.h
+++ b/src/calf/lv2wrap.h
@@ -64,6 +64,18 @@ struct lv2_instance: public Module, public plugin_ctl_iface
     virtual bool activate_preset(int bank, int program) { 
         return false;
     }
+    virtual const char *get_name()
+    {
+        return Module::get_name();
+    }
+    virtual const char *get_id()
+    {
+        return Module::get_id();
+    }
+    virtual int get_input_count() { return Module::in_count; }
+    virtual int get_output_count() { return Module::out_count; }
+    virtual bool get_midi() { return Module::support_midi; }
+    virtual float get_level(int port) { return 0.f; }
 };
 
 template<class Module>
diff --git a/src/calf/main_win.h b/src/calf/main_win.h
new file mode 100644
index 0000000..a6303fa
--- /dev/null
+++ b/src/calf/main_win.h
@@ -0,0 +1,81 @@
+/* Calf DSP Library Utility Application - calfjackhost
+ * GUI - main window
+ *
+ * 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_MAIN_WIN_H
+#define __CALF_MAIN_WIN_H
+
+#include <expat.h>
+#include <map>
+#include <set>
+#include <vector>
+#include <gtk/gtk.h>
+#include <calf/gui.h>
+#include <calf/jackhost.h>
+#include "custom_ctl.h"
+
+namespace synth {
+
+    class main_window: public main_window_iface
+    {
+    public:
+        struct plugin_strip
+        {
+            main_window *main_win;
+            plugin_ctl_iface *plugin;
+            plugin_gui_window *gui_win;
+            GtkWidget *name, *midi_in, *audio_in[2], *audio_out[2];
+        };
+        
+    public:
+        GtkWindow *toplevel;
+        GtkWidget *all_vbox;
+        GtkWidget *strips_table;
+        jack_client *client;
+        std::map<plugin_ctl_iface *, plugin_strip *> plugins;
+        std::set<std::string> conditions;
+        std::vector<plugin_ctl_iface *> plugin_queue;
+        std::string prefix;
+        bool is_closed;
+        int source_id;
+
+    protected:
+        plugin_strip *create_strip(plugin_ctl_iface *plugin);
+        void update_strip(plugin_ctl_iface *plugin);
+        static gboolean on_idle(void *data);
+
+    public:
+        main_window();
+        void add_plugin(plugin_ctl_iface *plugin);
+        void del_plugin(plugin_ctl_iface *plugin);
+        void set_window(plugin_ctl_iface *iface, plugin_gui_window *window);
+        void refresh_all_presets();
+        void refresh_plugin(plugin_ctl_iface *plugin);
+        void on_closed();
+        void close_guis();
+        void open_gui(plugin_ctl_iface *plugin);
+        bool check_condition(const char *cond) {
+            return conditions.count(cond) != 0;
+        }
+    
+        void create();
+    };
+};
+
+#endif
diff --git a/src/calf/modules.h b/src/calf/modules.h
index 4b3b7fb..a9287cc 100644
--- a/src/calf/modules.h
+++ b/src/calf/modules.h
@@ -68,6 +68,8 @@ public:
         }
         return inputs_mask;
     }
+    static const char *get_name() { return "amp"; }
+    static const char *get_id() { return "amp"; }
 };
 
 class flanger_audio_module: public null_audio_module
@@ -112,6 +114,8 @@ public:
         right.process(outs[1] + offset, ins[1] + offset, nsamples);
         return outputs_mask; // XXXKF allow some delay after input going blank
     }
+    static const char *get_name() { return "flanger"; }
+    static const char *get_id() { return "flanger"; }
 };
 
 class reverb_audio_module: public null_audio_module
@@ -159,6 +163,8 @@ public:
         reverb.extra_sanitize();
         return outputs_mask;
     }
+    static const char *get_name() { return "reverb"; }
+    static const char *get_id() { return "reverb"; }
 };
 
 class filter_audio_module: public null_audio_module
@@ -308,6 +314,8 @@ public:
         }
         return ostate;
     }
+    static const char *get_id() { return "filter"; }
+    static const char *get_name() { return "filter"; }
 };
 
 class vintage_delay_audio_module: public null_audio_module
@@ -418,6 +426,8 @@ public:
         }
         return ostate;
     }
+    static const char *get_name() { return "vintage_delay"; }
+    static const char *get_id() { return "vintagedelay"; }
 };
 
 extern std::string get_builtin_modules_rdf();
diff --git a/src/calf/modules_dev.h b/src/calf/modules_dev.h
index a9a8bb8..b3fc394 100644
--- a/src/calf/modules_dev.h
+++ b/src/calf/modules_dev.h
@@ -73,7 +73,8 @@ public:
         render_to(o, nsamples);
         return 3;
     }
-    
+    static const char *get_name() { return "organ"; }    
+    static const char *get_id() { return "organ"; }    
 };
 
 class rotary_speaker_audio_module: public null_audio_module
@@ -238,6 +239,8 @@ public:
             return;
         }
     }
+    static const char *get_name() { return "rotary_speaker"; }
+    static const char *get_id() { return "rotaryspeaker"; }
 };
 
 };
diff --git a/src/calf/modules_synths.h b/src/calf/modules_synths.h
index c5cce96..2f67bec 100644
--- a/src/calf/modules_synths.h
+++ b/src/calf/modules_synths.h
@@ -182,6 +182,8 @@ public:
             
         return 3;
     }
+    static const char *get_name() { return "monosynth"; }
+    static const char *get_id() { return "monosynth"; }
 };
 
 };
diff --git a/src/custom_ctl.cpp b/src/custom_ctl.cpp
index f95d7ae..ebaa80d 100644
--- a/src/custom_ctl.cpp
+++ b/src/custom_ctl.cpp
@@ -155,7 +155,7 @@ calf_line_graph_get_type (void)
         GTypeInfo *type_info_copy = new GTypeInfo(type_info);
 
         for (int i = 0; ; i++) {
-            char *name = g_strdup_printf("CalfLineGraph%d", i);
+            char *name = g_strdup_printf("CalfLineGraph%u%d", ((unsigned int)calf_line_graph_class_init) >> 16, i);
             if (g_type_from_name(name)) {
                 free(name);
                 continue;
@@ -171,6 +171,147 @@ calf_line_graph_get_type (void)
     return type;
 }
 
+///////////////////////////////////////// vu meter ///////////////////////////////////////////////
+
+static gboolean
+calf_vumeter_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+    g_assert(CALF_IS_VUMETER(widget));
+    
+    CalfVUMeter *vu = CALF_VUMETER(widget);
+    int ox = widget->allocation.x + 1, oy = widget->allocation.y + 1;
+    int sx = widget->allocation.width - 2, sy = widget->allocation.height - 2;
+    
+    cairo_t *c = gdk_cairo_create(GDK_DRAWABLE(widget->window));
+
+    GdkColor sc = { 0, 0, 0, 0 };
+    gdk_cairo_set_source_color(c, &sc);
+    cairo_rectangle(c, ox, oy, sx, sy);
+    cairo_fill(c);
+    cairo_set_line_width(c, 1);
+    
+    for (int x = ox; x <= ox + sx; x += 3)
+    {
+        float ts = (x - ox) * 1.0 / sx;
+        int is = vu->value > ts ? 2 : 1;
+        float r, g, b;
+        if (ts < 0.75)
+            r = ts / 0.75, g = 1, b = 0;
+        else
+            r = 1, g = 1 - (ts - 0.75) / 0.25, b = 0;
+        if (vu->value < ts || vu->value <= 0)
+            r *= 0.5, g *= 0.5, b *= 0.5;
+        GdkColor sc2 = { 0, 65535 * r, 65535 * g, 65535 * b };
+        gdk_cairo_set_source_color(c, &sc2);
+        cairo_move_to(c, x, oy);
+        cairo_line_to(c, x, oy + sy + 1);
+        cairo_move_to(c, x, oy + sy);
+        cairo_line_to(c, x, oy );
+        cairo_stroke(c);
+    }
+
+    cairo_destroy(c);
+    
+    gtk_paint_shadow(widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, widget, NULL, ox - 1, oy - 1, sx + 2, sy + 2);
+    // printf("exposed %p %d+%d\n", widget->window, widget->allocation.x, widget->allocation.y);
+    
+    return TRUE;
+}
+
+static void
+calf_vumeter_size_request (GtkWidget *widget,
+                           GtkRequisition *requisition)
+{
+    g_assert(CALF_IS_VUMETER(widget));
+    
+    requisition->width = 50;
+    requisition->height = 14;
+}
+
+static void
+calf_vumeter_size_allocate (GtkWidget *widget,
+                           GtkAllocation *allocation)
+{
+    g_assert(CALF_IS_VUMETER(widget));
+    
+    widget->allocation = *allocation;
+}
+
+static void
+calf_vumeter_class_init (CalfVUMeterClass *klass)
+{
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+    widget_class->expose_event = calf_vumeter_expose;
+    widget_class->size_request = calf_vumeter_size_request;
+    widget_class->size_allocate = calf_vumeter_size_allocate;
+}
+
+static void
+calf_vumeter_init (CalfVUMeter *self)
+{
+    GtkWidget *widget = GTK_WIDGET(self);
+    GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
+    widget->requisition.width = 40;
+    widget->requisition.height = 40;
+    self->value = 0.5;
+}
+
+GtkWidget *
+calf_vumeter_new()
+{
+    return GTK_WIDGET( g_object_new (CALF_TYPE_VUMETER, NULL ));
+}
+
+GType
+calf_vumeter_get_type (void)
+{
+    static GType type = 0;
+    if (!type) {
+        static const GTypeInfo type_info = {
+            sizeof(CalfVUMeterClass),
+            NULL, /* base_init */
+            NULL, /* base_finalize */
+            (GClassInitFunc)calf_vumeter_class_init,
+            NULL, /* class_finalize */
+            NULL, /* class_data */
+            sizeof(CalfVUMeter),
+            0,    /* n_preallocs */
+            (GInstanceInitFunc)calf_vumeter_init
+        };
+
+        GTypeInfo *type_info_copy = new GTypeInfo(type_info);
+
+        for (int i = 0; ; i++) {
+            char *name = g_strdup_printf("CalfVUMeter%u%d", ((unsigned int)calf_vumeter_class_init) >> 16, i);
+            if (g_type_from_name(name)) {
+                free(name);
+                continue;
+            }
+            type = g_type_register_static( GTK_TYPE_WIDGET,
+                                           name,
+                                           type_info_copy,
+                                           (GTypeFlags)0);
+            free(name);
+            break;
+        }
+    }
+    return type;
+}
+
+extern void calf_vumeter_set_value(CalfVUMeter *meter, float value)
+{
+    if (value != meter->value)
+    {
+        meter->value = value;
+        gtk_widget_queue_draw(GTK_WIDGET(meter));
+    }
+}
+
+extern float calf_vumeter_get_value(CalfVUMeter *meter)
+{
+    return meter->value;
+}
+
 ///////////////////////////////////////// knob ///////////////////////////////////////////////
 
 static gboolean
@@ -386,7 +527,8 @@ calf_knob_get_type (void)
         };
         
         for (int i = 0; ; i++) {
-            char *name = g_strdup_printf("CalfKnob%d", i);
+            char *name = g_strdup_printf("CalfKnob%u%d", 
+                ((unsigned int)calf_knob_class_init) >> 16, i);
             if (g_type_from_name(name)) {
                 free(name);
                 continue;
diff --git a/src/dssigui.cpp b/src/dssigui.cpp
index 70f2113..d249015 100644
--- a/src/dssigui.cpp
+++ b/src/dssigui.cpp
@@ -26,6 +26,7 @@
 #include <calf/modules.h>
 #include <calf/modules_dev.h>
 #include <calf/benchmark.h>
+#include <calf/main_win.h>
 
 using namespace std;
 using namespace dsp;
@@ -132,6 +133,18 @@ struct plugin_proxy: public plugin_proxy_base, public line_graph_iface
     virtual bool get_graph(int index, int subindex, float *data, int points, cairo_t *context) {
         return Module::get_static_graph(index, subindex, params[index], data, points, context);
     }
+    virtual const char *get_name()
+    {
+        return Module::get_name();
+    }
+    virtual const char *get_id()
+    {
+        return Module::get_id();
+    }
+    virtual int get_input_count() { return Module::in_count; }
+    virtual int get_output_count() { return Module::out_count; }
+    virtual bool get_midi() { return Module::support_midi; }
+    virtual float get_level(int port) { return 0.f; }
 };
 
 plugin_proxy_base *create_plugin_proxy(const char *effect_name)
@@ -175,7 +188,8 @@ static bool osc_debug = false;
 struct dssi_osc_server: public osc_server, public osc_message_sink
 {
     plugin_proxy_base *plugin;
-    plugin_gui_window window;
+    main_window *main_win;
+    plugin_gui_window *window;
     string effect_name, title;
     osc_client cli;
     bool in_program, enable_dump;
@@ -183,6 +197,8 @@ struct dssi_osc_server: public osc_server, public osc_message_sink
     
     dssi_osc_server()
     : plugin(NULL)
+    , main_win(new main_window)
+    , window(new plugin_gui_window(main_win))
     {
         sink = this;
     }
@@ -202,10 +218,10 @@ struct dssi_osc_server: public osc_server, public osc_message_sink
         plugin = create_plugin_proxy(effect_name.c_str());
         plugin->client = &cli;
         plugin->send_osc = true;
-        window.conditions.insert("dssi");
-        window.create(plugin, title.c_str(), effect_name.c_str());
-        plugin->gui = window.gui;
-        gtk_signal_connect(GTK_OBJECT(window.toplevel), "destroy", G_CALLBACK(on_destroy), this);
+        ((main_window *)window->main)->conditions.insert("dssi");
+        window->create(plugin, title.c_str(), effect_name.c_str());
+        plugin->gui = window->gui;
+        gtk_signal_connect(GTK_OBJECT(window->toplevel), "destroy", G_CALLBACK(on_destroy), this);
         global_presets.get_for_plugin(presets, effect_name.c_str());
     }
     
@@ -236,7 +252,7 @@ struct dssi_osc_server: public osc_server, public osc_message_sink
                 for (int i =0 ; i < count; i++)
                     plugin->set_param_value(i, plugin->get_param_props(i)->def_value);
                 plugin->send_osc = sosc;
-                window.gui->refresh();
+                window->gui->refresh();
                 // special handling for default preset
                 return;
             }
@@ -247,7 +263,7 @@ struct dssi_osc_server: public osc_server, public osc_message_sink
             plugin->send_osc = false;
             presets[nr].activate(plugin);
             plugin->send_osc = sosc;
-            window.gui->refresh();
+            window->gui->refresh();
             
             // cli.send("/update", data);
             return;
@@ -259,18 +275,18 @@ struct dssi_osc_server: public osc_server, public osc_message_sink
             debug_printf("CONTROL %d %f\n", idx, val);
             bool sosc = plugin->send_osc;
             plugin->send_osc = false;
-            window.gui->set_param_value(idx, val);
+            window->gui->set_param_value(idx, val);
             plugin->send_osc = sosc;
             return;
         }
         if (address == prefix + "/show")
         {
-            gtk_widget_show_all(GTK_WIDGET(window.toplevel));
+            gtk_widget_show_all(GTK_WIDGET(window->toplevel));
             return;
         }
         if (address == prefix + "/hide")
         {
-            gtk_widget_hide(GTK_WIDGET(window.toplevel));
+            gtk_widget_hide(GTK_WIDGET(window->toplevel));
             return;
         }
     }
diff --git a/src/gui.cpp b/src/gui.cpp
index 232a9b7..cbf13d1 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -19,10 +19,12 @@
  */
  
 #include <config.h>
+#include <assert.h>
 #include <calf/giface.h>
 #include <calf/gui.h>
 #include <calf/preset.h>
 #include <calf/preset_gui.h>
+#include <calf/main_win.h>
 
 using namespace synth;
 using namespace std;
@@ -49,6 +51,14 @@ void param_control::hook_params()
         gui->add_param_ctl(param_no, this);
 }
 
+param_control::~param_control()
+{
+    if (label)
+        gtk_widget_destroy(label);
+    if (widget)
+        gtk_widget_destroy(widget);
+}
+
 // combo box
 
 GtkWidget *combo_box_param_control::create(plugin_gui *_gui, int _param_no)
@@ -290,11 +300,10 @@ void knob_param_control::knob_value_changed(GtkWidget *widget, gpointer value)
 
 // line graph
 
-gboolean line_graph_param_control::update(void *data)
+void line_graph_param_control::on_idle()
 {
-    line_graph_param_control *self = (line_graph_param_control *)data;
-    self->set();
-    return TRUE;
+    if (get_int("refresh", 0))
+        set();
 }
 
 GtkWidget *line_graph_param_control::create(plugin_gui *_gui, int _param_no)
@@ -309,10 +318,7 @@ GtkWidget *line_graph_param_control::create(plugin_gui *_gui, int _param_no)
     widget->requisition.height = get_int("height", 40);
     clg->source = gui->plugin->get_line_graph_iface();
     clg->source_id = param_no;
-    
-    if (get_int("refresh", 0))
-        source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, update, this, NULL); // 30 fps should be enough for everybody
-    
+        
     return widget;
 }
 
@@ -330,8 +336,6 @@ void line_graph_param_control::set()
 
 line_graph_param_control::~line_graph_param_control()
 {
-    if (get_int("refresh", 0))
-        g_source_remove(source_id);
 }
 
 /******************************** GUI proper ********************************/
@@ -342,6 +346,11 @@ plugin_gui::plugin_gui(plugin_gui_window *_window)
     
 }
 
+static void window_destroyed(GtkWidget *window, gpointer data)
+{
+    delete (plugin_gui_window *)data;
+}
+
 static void action_destroy_notify(gpointer data)
 {
     delete (activate_preset_params *)data;
@@ -571,12 +580,12 @@ void plugin_gui::xml_element_start(const char *element, const char *attributes[]
         if (!xam.count("cond") || xam["cond"].empty())
             g_error("Incorrect <if cond=\"[!]symbol\"> element");
         string cond = xam["cond"];
-        unsigned int exp_count = 1;
+        bool state = true;
         if (cond.substr(0, 1) == "!") {
-            exp_count = 0;
+            state = false;
             cond.erase(0, 1);
         }
-        if (window->conditions.count(cond) == exp_count)
+        if (window->main->check_condition(cond.c_str()) == state)
             return;
         ignore_stack = 1;
         return;
@@ -667,6 +676,15 @@ GtkWidget *plugin_gui::create_from_xml(plugin_ctl_iface *_plugin, const char *xm
     return GTK_WIDGET(top_container->container);
 }
 
+void plugin_gui::on_idle()
+{
+    for (unsigned int i = 0; i < params.size(); i++)
+    {
+        if (params[i] != NULL)
+            params[i]->on_idle();
+    }    
+}
+
 void plugin_gui::refresh()
 {
     for (unsigned int i = 0; i < params.size(); i++)
@@ -693,6 +711,14 @@ void plugin_gui::set_param_value(int param_no, float value, param_control *origi
     refresh(param_no);
 }
 
+plugin_gui::~plugin_gui()
+{
+    for (std::vector<param_control *>::iterator i = params.begin(); i != params.end(); i++)
+    {
+        delete *i;
+    }
+}
+
 
 /******************************* Actions **************************************************/
  
@@ -701,14 +727,7 @@ static void store_preset_action(GtkAction *action, plugin_gui_window *gui_win)
     store_preset(GTK_WINDOW(gui_win->toplevel), gui_win->gui);
 }
 
-static void exit_gui(GtkAction *action, plugin_gui_window *gui_win)
-{
-    gtk_widget_destroy(GTK_WIDGET(gui_win->toplevel));
-}
-
 static const GtkActionEntry actions[] = {
-    { "HostMenuAction", "", "_Host", NULL, "Application-wide actions", NULL },
-    { "exit", "", "E_xit", NULL, "Exit the application", (GCallback)exit_gui },
     { "PresetMenuAction", "", "_Preset", NULL, "Preset operations", NULL },
     { "store-preset", "", "_Store preset", NULL, "Store a current setting as preset", (GCallback)store_preset_action },
 };
@@ -718,9 +737,6 @@ static const GtkActionEntry actions[] = {
 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"
@@ -743,12 +759,14 @@ static const char *preset_post_xml =
 "</ui>\n"
 ;
 
-plugin_gui_window::plugin_gui_window()
+plugin_gui_window::plugin_gui_window(main_window_iface *_main)
 {
     toplevel = NULL;
     ui_mgr = NULL;
     std_actions = NULL;
     preset_actions = NULL;
+    main = _main;
+    assert(main);
 }
 
 string plugin_gui_window::make_gui_preset_list(GtkActionGroup *grp)
@@ -784,6 +802,13 @@ void plugin_gui_window::fill_gui_presets()
     gtk_ui_manager_add_ui_from_string(ui_mgr, preset_xml.c_str(), -1, &error);
 }
 
+gboolean plugin_gui_window::on_idle(void *data)
+{
+    plugin_gui_window *self = (plugin_gui_window *)data;
+    self->gui->on_idle();
+    return TRUE;
+}
+
 void plugin_gui_window::create(plugin_ctl_iface *_jh, const char *title, const char *effect)
 {
     toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
@@ -836,20 +861,25 @@ void plugin_gui_window::create(plugin_ctl_iface *_jh, const char *title, const c
     //gtk_widget_set_size_request(GTK_WIDGET(toplevel), max(req.width + 10, req2.width), req.height + req2.height + 10);
     // printf("size set %dx%d\n", wx, wy);
     // gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(sw), GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, req.height, 20, 100, 100)));
-    all_windows.insert(this);
+    gtk_signal_connect (GTK_OBJECT (toplevel), "destroy", G_CALLBACK (window_destroyed), (plugin_gui_window *)this);
+    main->set_window(gui->plugin, this);
+    source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
 }
 
-plugin_gui_window::~plugin_gui_window()
+void plugin_gui_window::close()
 {
-    all_windows.erase(this);
-    delete gui;
+    if (source_id)
+        g_source_remove(source_id);
+    source_id = 0;
+    gtk_widget_destroy(GTK_WIDGET(toplevel));
 }
 
-std::set<plugin_gui_window *> plugin_gui_window::all_windows;
-
-void plugin_gui_window::refresh_all_presets()
+plugin_gui_window::~plugin_gui_window()
 {
-    for (std::set<plugin_gui_window *>::iterator i = all_windows.begin(); i != all_windows.end(); i++)
-        (*i)->fill_gui_presets();
+    if (source_id)
+        g_source_remove(source_id);
+    main->set_window(gui->plugin, NULL);
+    delete gui;
 }
 
+
diff --git a/src/jackhost.cpp b/src/jackhost.cpp
index b697541..69d0d8a 100644
--- a/src/jackhost.cpp
+++ b/src/jackhost.cpp
@@ -34,6 +34,7 @@
 #include <calf/gui.h>
 #include <calf/preset.h>
 #include <calf/preset_gui.h>
+#include <calf/main_win.h>
 
 using namespace synth;
 using namespace std;
@@ -70,6 +71,10 @@ void destroy(GtkWindow *window, gpointer data)
     gtk_main_quit();
 }
 
+void gui_win_destroy(GtkWindow *window, gpointer data)
+{
+}
+
 static struct option long_options[] = {
     {"help", 0, 0, 'h'},
     {"version", 0, 0, 'v'},
@@ -121,7 +126,7 @@ struct host_session
     string autoconnect_midi;
     set<int> chains;
     vector<jack_host_base *> plugins;
-    vector<plugin_gui_window *> guis;
+    main_window *main_win;
     int lash_source_id;
     bool restoring_session;
     
@@ -131,6 +136,7 @@ struct host_session
     void close();
     static gboolean update_lash(void *self) { ((host_session *)self)->update_lash(); return TRUE; }
     void update_lash();
+    void activate_preset(int plugin, const std::string &preset);
 #if USE_LASH
     void send_lash(LASH_Event_Type type, const std::string &data) {
         lash_send_event(lash_client, lash_event_new_with_all(type, data.c_str()));
@@ -152,6 +158,7 @@ host_session::host_session()
     lash_args = NULL;
     lash_source_id = 0;
     restoring_session = false;
+    main_win = new main_window;
 }
 
 void host_session::open()
@@ -160,6 +167,9 @@ void host_session::open()
     if (!output_name.empty()) client.output_name = output_name;
     if (!midi_name.empty()) client.midi_name = midi_name;
     client.open(client_name.c_str());
+    main_win->prefix = client_name + " - ";
+    main_win->conditions.insert("jackhost");
+    main_win->conditions.insert("directlink");
     for (unsigned int i = 0; i < plugin_names.size(); i++) {
         // if (presets.count(i))
         //    printf("%s : %s\n", names[i].c_str(), presets[i].c_str());
@@ -172,34 +182,33 @@ void host_session::open()
 #endif
         }
         jh->open(&client);
-        gui_win = new plugin_gui_window;
-        gui_win->conditions.insert("jackhost");
-        gui_win->conditions.insert("directlink");
-        gui_win->create(jh, (string(client_name)+" - "+plugin_names[i]).c_str(), plugin_names[i].c_str());
-        gtk_signal_connect(GTK_OBJECT(gui_win->toplevel), "destroy", G_CALLBACK(destroy), NULL);
-        gtk_widget_show_all(GTK_WIDGET(gui_win->toplevel));
-        guis.push_back(gui_win);
         plugins.push_back(jh);
         client.add(jh);
-        if (presets.count(i)) {
-            string cur_plugin = plugin_names[i];
-            string preset = presets[i];
-            preset_vector &pvec = global_presets.presets;
-            bool found = false;
-            for (unsigned int i = 0; i < pvec.size(); i++) {
-                if (pvec[i].name == preset && pvec[i].plugin == cur_plugin)
-                {
-                    pvec[i].activate(jh);
-                    gui_win->gui->refresh();
-                    found = true;
-                    break;
-                }
-            }
-            if (!found) {
-                fprintf(stderr, "Warning: unknown preset %s %s\n", preset.c_str(), cur_plugin.c_str());
-            }
+        main_win->add_plugin(jh);
+        if (presets.count(i))
+            activate_preset(i, presets[i]);
+    }
+    main_win->create();
+    gtk_signal_connect(GTK_OBJECT(main_win->toplevel), "destroy", G_CALLBACK(destroy), NULL);
+}
+
+void host_session::activate_preset(int i, const std::string &preset)
+{
+    string cur_plugin = plugin_names[i];
+    preset_vector &pvec = global_presets.presets;
+    bool found = false;
+    for (unsigned int i = 0; i < pvec.size(); i++) {
+        if (pvec[i].name == preset && pvec[i].plugin == cur_plugin)
+        {
+            pvec[i].activate(plugins[i]);
+            gui_win->gui->refresh();
+            found = true;
+            break;
         }
     }
+    if (!found) {
+        fprintf(stderr, "Warning: unknown preset %s %s\n", preset.c_str(), cur_plugin.c_str());
+    }
 }
 
 void host_session::connect()
@@ -268,8 +277,9 @@ void host_session::close()
         g_source_remove(lash_source_id);
     client.deactivate();
     client.close();
+    main_win->on_closed();
+    main_win->close_guis();
     for (unsigned int i = 0; i < plugin_names.size(); i++) {
-        delete guis[i];
         plugins[i]->close();
         delete plugins[i];
     }
@@ -320,7 +330,7 @@ void host_session::update_lash()
                             if (tmp.presets.size())
                             {
                                 tmp.presets[0].activate(plugins[nplugin]);
-                                guis[nplugin]->gui->refresh();
+                                main_win->refresh_plugin(plugins[nplugin]);
                             }
                         }
                     }
diff --git a/src/lv2gui.cpp b/src/lv2gui.cpp
index e0c315c..6304fcd 100644
--- a/src/lv2gui.cpp
+++ b/src/lv2gui.cpp
@@ -23,6 +23,7 @@
 #include <config.h>
 #include <calf/giface.h>
 #include <calf/gui.h>
+#include <calf/main_win.h>
 #include <calf/modules.h>
 #include <calf/modules_dev.h>
 #include <calf/benchmark.h>
@@ -84,24 +85,42 @@ struct plugin_proxy: public plugin_proxy_base, public line_graph_iface
             send = true;
         }
     }
-    virtual int get_param_count() {
+    virtual int get_param_count()
+    {
         return Module::param_count;
     }
-    virtual int get_param_port_offset() {
+    virtual int get_param_port_offset()
+    {
         return Module::in_count + Module::out_count;
     }
-    virtual const char *get_gui_xml() {
+    virtual const char *get_gui_xml()
+    {
         return Module::get_gui_xml();
     }
-    virtual line_graph_iface *get_line_graph_iface() {
+    virtual line_graph_iface *get_line_graph_iface()
+    {
         return this;
     }
-    virtual bool activate_preset(int bank, int program) { 
+    virtual bool activate_preset(int bank, int program)
+    {
         return false;
     }
-    virtual bool get_graph(int index, int subindex, float *data, int points, cairo_t *context) {
+    virtual bool get_graph(int index, int subindex, float *data, int points, cairo_t *context) 
+    {
         return Module::get_static_graph(index, subindex, params[index], data, points, context);
     }
+    virtual const char *get_name()
+    {
+        return Module::get_name();
+    }
+    virtual const char *get_id()
+    {
+        return Module::get_id();
+    }
+    virtual int get_input_count() { return Module::in_count; }
+    virtual int get_output_count() { return Module::out_count; }
+    virtual bool get_midi() { return Module::support_midi; }
+    virtual float get_level(int port) { return 0.f; }
 };
 
 plugin_proxy_base *create_plugin_proxy(const char *effect_name)
@@ -140,8 +159,9 @@ LV2UI_Handle gui_instantiate(const struct _LV2UI_Descriptor* descriptor,
     plugin_proxy_base *proxy = create_plugin_proxy(plugin_uri + sizeof("http://calf.sourceforge.net/plugins/") - 1);
     proxy->setup(write_function, controller);
     // dummy window
-    plugin_gui_window *window = new plugin_gui_window;
-    window->conditions.insert("lv2gui");
+    main_window *main = new main_window;
+    main->conditions.insert("lv2gui");    
+    plugin_gui_window *window = new plugin_gui_window(main);
     plugin_gui *gui = new plugin_gui(window);
     const char *xml = proxy->get_gui_xml();
     if (xml)
diff --git a/src/main_win.cpp b/src/main_win.cpp
new file mode 100644
index 0000000..70a6cf1
--- /dev/null
+++ b/src/main_win.cpp
@@ -0,0 +1,233 @@
+/* Calf DSP Library
+ * GUI main window.
+ * 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.
+ */
+ 
+#include <assert.h>
+#include <config.h>
+#include <calf/giface.h>
+#include <calf/gui.h>
+#include <calf/preset.h>
+#include <calf/preset_gui.h>
+#include <calf/main_win.h>
+
+using namespace synth;
+using namespace std;
+
+main_window::main_window()
+{
+    toplevel = NULL;
+    is_closed = true;
+}
+
+void main_window::add_plugin(plugin_ctl_iface *plugin)
+{
+    if (toplevel)
+    {
+        plugins[plugin] = create_strip(plugin);
+    }
+    else {
+        plugin_queue.push_back(plugin);
+        plugins[plugin] = NULL;
+    }
+}
+
+void main_window::del_plugin(plugin_ctl_iface *plugin)
+{
+    plugins.erase(plugin);
+}
+
+void main_window::set_window(plugin_ctl_iface *plugin, plugin_gui_window *gui_win)
+{
+    if (!plugins.count(plugin))
+        return;
+    plugin_strip *strip = plugins[plugin];
+    if (!strip)
+        return;
+    strip->gui_win = gui_win;
+    if (!is_closed)
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(strip->name), gui_win != NULL);
+}
+
+void main_window::refresh_all_presets()
+{
+    for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
+    {
+        if (i->second && i->second->gui_win)
+            i->second->gui_win->fill_gui_presets();
+    }
+}
+
+static gboolean
+gui_button_pressed(GtkWidget *button, main_window::plugin_strip *strip)
+{
+    GtkToggleButton *tb = GTK_TOGGLE_BUTTON(button);
+    if ((gtk_toggle_button_get_active(tb) != 0) == (strip->gui_win != NULL))
+        return FALSE;
+    if (strip->gui_win) {
+        strip->gui_win->close();
+        strip->gui_win = NULL;
+    } else {
+        strip->main_win->open_gui(strip->plugin);
+    }
+    return TRUE;
+}
+
+main_window::plugin_strip *main_window::create_strip(plugin_ctl_iface *plugin)
+{
+    plugin_strip *strip = new plugin_strip;
+    strip->main_win = this;
+    strip->plugin = plugin;
+    strip->gui_win = NULL;
+    
+    int row = 0, cols = 0;
+    g_object_get(G_OBJECT(strips_table), "n-rows", &row, "n-columns", &cols, NULL);
+    gtk_table_resize(GTK_TABLE(strips_table), row + 3, cols);
+
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), gtk_hseparator_new(), 0, 4, row, row + 1);
+    row++;
+    
+    GtkWidget *label = gtk_toggle_button_new_with_label(plugin->get_name());
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), label, 0, 1, row, row + 2);
+    strip->name = label;
+    gtk_signal_connect(GTK_OBJECT(label), "toggled", G_CALLBACK(gui_button_pressed), 
+        (plugin_ctl_iface *)strip);
+    
+    label = gtk_label_new(plugin->get_midi() ? "MIDI" : "");
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), label, 1, 2, row, row + 2);
+    strip->midi_in = label;
+
+    for (int i = 0; i < 2; i++)
+        strip->audio_in[i] = strip->audio_out[i] = NULL;
+    
+    if (plugin->get_input_count() == 2) {
+        label = calf_vumeter_new();
+        gtk_table_attach_defaults(GTK_TABLE(strips_table), label, 2, 3, row, row + 1);
+        strip->audio_in[0] = label;
+        label = calf_vumeter_new();
+        gtk_table_attach_defaults(GTK_TABLE(strips_table), label, 2, 3, row + 1, row + 2);
+        strip->audio_in[1] = label;
+    }
+
+    if (plugin->get_output_count() == 2) {
+        label = calf_vumeter_new();
+        gtk_table_attach_defaults(GTK_TABLE(strips_table), label, 3, 4, row, row + 1);
+        strip->audio_out[0] = label;
+        label = calf_vumeter_new();
+        gtk_table_attach_defaults(GTK_TABLE(strips_table), label, 3, 4, row + 1, row + 2);
+        strip->audio_out[1] = label;
+    }
+    return strip;
+}
+
+void main_window::update_strip(plugin_ctl_iface *plugin)
+{
+    // plugin_strip *strip = plugins[plugin];
+    // assert(strip);
+    
+}
+
+void main_window::open_gui(plugin_ctl_iface *plugin)
+{
+    plugin_gui_window *gui_win = new plugin_gui_window(this);
+    gui_win->create(plugin, (prefix + plugin->get_name()).c_str(), plugin->get_id());
+    gtk_widget_show_all(GTK_WIDGET(gui_win->toplevel));
+    plugins[plugin]->gui_win = gui_win; 
+}
+
+void main_window::create()
+{
+    toplevel = GTK_WINDOW(gtk_window_new (GTK_WINDOW_TOPLEVEL));
+    is_closed = false;
+    
+    all_vbox = gtk_vbox_new(0, FALSE);
+    strips_table = gtk_table_new(1, 5, FALSE);
+    gtk_table_set_col_spacings(GTK_TABLE(strips_table), 10);
+    gtk_table_set_row_spacings(GTK_TABLE(strips_table), 5);
+    
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), gtk_label_new("Module"), 0, 1, 0, 1);
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), gtk_label_new("MIDI In"), 1, 2, 0, 1);
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), gtk_label_new("Audio In"), 2, 3, 0, 1);
+    gtk_table_attach_defaults(GTK_TABLE(strips_table), gtk_label_new("Audio Out"), 3, 4, 0, 1);
+    for (std::vector<plugin_ctl_iface *>::iterator i = plugin_queue.begin(); i != plugin_queue.end(); i++)
+    {
+        plugins[*i] = create_strip(*i);
+        update_strip(*i);        
+    }
+
+    gtk_container_add(GTK_CONTAINER(all_vbox), strips_table);
+    gtk_container_add(GTK_CONTAINER(toplevel), all_vbox);
+    
+    gtk_widget_show_all(GTK_WIDGET(toplevel));
+    source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, on_idle, this, NULL); // 30 fps should be enough for everybody
+}
+
+void main_window::refresh_plugin(plugin_ctl_iface *plugin)
+{
+    plugins[plugin]->gui_win->gui->refresh();
+}
+
+void main_window::close_guis()
+{
+    for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = plugins.begin(); i != plugins.end(); i++)
+    {
+        if (i->second && i->second->gui_win) {
+            i->second->gui_win->close();
+        }
+    }
+    plugins.clear();
+}
+
+void main_window::on_closed()
+{
+    if (source_id)
+        g_source_remove(source_id);
+    is_closed = true;
+    toplevel = NULL;
+}
+
+static inline float LVL(float value)
+{
+    return sqrt(value) * 0.75;
+}
+
+gboolean main_window::on_idle(void *data)
+{
+    main_window *self = (main_window *)data;
+    for (std::map<plugin_ctl_iface *, plugin_strip *>::iterator i = self->plugins.begin(); i != self->plugins.end(); i++)
+    {
+        if (i->second)
+        {
+            plugin_ctl_iface *plugin = i->first;
+            plugin_strip *strip = i->second;
+            int idx = 0;
+            if (plugin->get_input_count() == 2) {
+                calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[0]), LVL(plugin->get_level(idx++)));
+                calf_vumeter_set_value(CALF_VUMETER(strip->audio_in[1]), LVL(plugin->get_level(idx++)));
+            }
+            if (plugin->get_output_count() == 2) {
+                calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[0]), LVL(plugin->get_level(idx++)));
+                calf_vumeter_set_value(CALF_VUMETER(strip->audio_out[1]), LVL(plugin->get_level(idx++)));
+            }
+            if (plugin->get_midi()) {
+                gtk_label_set_text(GTK_LABEL(strip->midi_in), (plugin->get_level(idx++) > 0.f) ? "*" : "");
+            }
+        }
+    }
+    return TRUE;
+}
diff --git a/src/preset_gui.cpp b/src/preset_gui.cpp
index 62a23bc..d63a727 100644
--- a/src/preset_gui.cpp
+++ b/src/preset_gui.cpp
@@ -24,6 +24,7 @@
 #include <calf/gui.h>
 #include <calf/preset.h>
 #include <calf/preset_gui.h>
+#include <calf/main_win.h>
 
 using namespace synth;
 using namespace std;
@@ -58,7 +59,7 @@ void store_preset_ok(GtkAction *action, plugin_gui *gui)
     global_presets = tmp;
     global_presets.save(tmp.get_preset_filename().c_str());
     gtk_widget_destroy(store_preset_dlg);
-    plugin_gui_window::refresh_all_presets();
+    gui->window->main->refresh_all_presets();
 }
 
 void store_preset_cancel(GtkAction *action, plugin_gui *gui)

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list