[SCM] calf/master: + autotools config fixed (fixed typo, added untested expat detection) + JACK GUI refactoring into separate "GUI window" class (not as good as should be, but still better than it was before) + preset menu updates after adding + presets are supposed to be loaded from global file in /usr/lib (still no global file, but one can't have everything :), I need to create those presets) + envelope fix (more like hack, eliminate disastrous rounding errors by using double instead of float, and treat 0% sustain specially so that the envelope doesn't get stuck in 0% sustain) - unneeded printf in monosynth (hope it won't get into commit, will revert it next time if it gets in)

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


The following commit has been merged in the master branch:
commit ec0796d12af9e88c6202f936f2945d9bf0554286
Author: kfoltman <kfoltman at 78b06b96-2940-0410-b7fc-879d825d01d8>
Date:   Tue Dec 18 00:07:25 2007 +0000

    + autotools config fixed (fixed typo, added untested expat detection)
    + JACK GUI refactoring into separate "GUI window" class (not as good as should be, but still better than it was before)
    + preset menu updates after adding
    + presets are supposed to be loaded from global file in /usr/lib (still no global file, but one can't have everything :), I need to create those presets)
    + envelope fix (more like hack, eliminate disastrous rounding errors by using double instead of float, and treat 0% sustain specially so that the envelope doesn't get stuck in 0% sustain)
    - unneeded printf in monosynth (hope it won't get into commit, will revert it next time if it gets in)
    
    
    
    git-svn-id: https://calf.svn.sourceforge.net/svnroot/calf/trunk@27 78b06b96-2940-0410-b7fc-879d825d01d8

diff --git a/Makefile.am b/Makefile.am
index d6ea7aa..7114d1d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,7 +4,7 @@ SUBDIRS = $(SRCDIRS)
 
 distdir = $(PACKAGE)-$(VERSION)
 
-EXTRADIST = TODO autogen.sh
+EXTRA_DIST = TODO autogen.sh calf.glade
 
 clean-local:
 	rm -f *~ *.old
diff --git a/configure.in b/configure.in
index 1d722ee..3c252f6 100644
--- a/configure.in
+++ b/configure.in
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ(2.61)
-AC_INIT([calf],[0.0.6],[wdev at foltman.com])
+AC_INIT([calf],[0.0.7],[wdev at foltman.com])
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 
@@ -30,6 +30,9 @@ AC_CHECK_HEADER(ladspa.h, LADSPA_FOUND="yes", LADSPA_FOUND="no")
 
 AC_CHECK_HEADER(dssi.h, DSSI_FOUND="yes", DSSI_FOUND="no")
 
+AC_CHECK_HEADER(expat.h, true, AC_MSG_ERROR([Expat XML library not found]))
+AC_CHECK_LIB(expat, XML_Parse, true, AC_MSG_ERROR([Expat XML library not found]))
+
 AC_CHECK_HEADERS([jack/jack.h], have_jack_header=yes, have_jack_header=no)
 
 if test "$have_jack_header" = "yes"; then
diff --git a/src/Makefile.am b/src/Makefile.am
index 8c3185b..4e6478c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,7 @@ bin_PROGRAMS =
 noinst_PROGRAMS = calfbenchmark
 noinst_LTLIBRARIES = libcalf.la libcalfstatic.la libcalfgui.la
 
-AM_CXXFLAGS = -ffast-math -march=i686 -finline-limit=80
+AM_CXXFLAGS = -ffast-math -march=i686 -finline-limit=80 -DPKGLIBDIR=\"$(pkglibdir)\"
 
 if USE_JACK
 AM_CXXFLAGS += -DUSE_JACK=1 $(JACK_DEPS_CFLAGS) $(GUI_DEPS_CFLAGS)
@@ -43,19 +43,23 @@ endif
 calfbenchmark_SOURCES = benchmark.cpp
 calfbenchmark_LDADD = 
 
-libcalf_la_SOURCES = modules.cpp giface.cpp synth.cpp
-libcalf_la_LDFLAGS = -rpath $(ladspadir) -version-info 0:0:0
+libcalf_la_SOURCES = modules.cpp giface.cpp synth.cpp preset.cpp
+libcalf_la_LDFLAGS = -rpath $(ladspadir) -version-info 0:0:0 -lexpat
 
-libcalfstatic_la_SOURCES = modules.cpp giface.cpp synth.cpp
-libcalfstatic_la_LDFLAGS = -static
+libcalfstatic_la_SOURCES = modules.cpp giface.cpp synth.cpp preset.cpp
+libcalfstatic_la_LDFLAGS = -static -lexpat
 
-libcalfgui_la_SOURCES = gui.cpp
+libcalfgui_la_SOURCES = gui.cpp preset_gui.cpp
 libcalfgui_la_LDFLAGS = -static
 
 clean-local:
 	$(RM) -f calfjackhost *~
 
 install-data-hook:
+if USE_JACK
+	install -d -m 755 $(DESTDIR)$(pkglibdir)    
+	install -c -m 644 $(top_srcdir)/calf.glade $(DESTDIR)$(pkglibdir)
+endif
 if USE_LADSPA
 	install -d -m 755 $(DESTDIR)$(with_ladspa_dir)
 	install -d -m 755 $(DESTDIR)$(with_ladspa_rdf_dir)
@@ -69,6 +73,10 @@ endif
 
 #remove calf.so, calf.rdf and - if empty - ladspa dir in usr/share
 uninstall-hook:
+if USE_JACK
+	rm -f $(DESTDIR)$(pkglibdir)/calf.glade 
+	rmdir $(DESTDIR)$(pkglibdir)    
+endif
 if USE_LADSPA
 	$(RM) -f $(DESTDIR)$(with_ladspa_dir)/calf.so
 	$(RM) -f $(DESTDIR)$(with_ladspa_rdf_dir)/calf.rdf
diff --git a/src/calf/Makefile.am b/src/calf/Makefile.am
index 6c998d4..e58384d 100644
--- a/src/calf/Makefile.am
+++ b/src/calf/Makefile.am
@@ -1,3 +1,7 @@
 calfdir = $(includedir)/calf
 
-calf_HEADERS = audio_fx.h benchmark.h biquad.h buffer.h delay.h fft.h fixed_point.h giface.h gui.h inertia.h jackhost.h modules.h modules_dev.h modules_synths.h onepole.h organ.h osc.h primitives.h synth.h wave.h
+calf_HEADERS = audio_fx.h benchmark.h biquad.h buffer.h \
+    delay.h fft.h fixed_point.h giface.h gui.h inertia.h \
+    jackhost.h modules.h modules_dev.h modules_synths.h \
+    onepole.h organ.h osc.h preset.h preset_gui.h primitives.h \
+    synth.h wave.h
diff --git a/src/calf/gui.h b/src/calf/gui.h
index daa198c..e6d5dc9 100644
--- a/src/calf/gui.h
+++ b/src/calf/gui.h
@@ -105,18 +105,22 @@ struct knob_param_control: public param_control
 };
 #endif
 
+class plugin_gui_window;
+
 class plugin_gui
 {
 protected:
     int param_count;
 public:
-    GtkWidget *toplevel;
+    plugin_gui_window *window;
     GtkWidget *table;
+    const char *effect_name;
     plugin_ctl_iface *plugin;
     std::vector<param_control *> params;
 
-    plugin_gui(GtkWidget *_toplevel);
+    plugin_gui(plugin_gui_window *_window);
     GtkWidget *create(plugin_ctl_iface *_plugin, const char *title);
+
     void refresh();
 
 #if USE_PHAT
@@ -124,6 +128,21 @@ public:
 #endif
 };
 
+class plugin_gui_window
+{
+public:
+    plugin_gui *gui;
+    GtkWindow *toplevel;
+    GtkUIManager *ui_mgr;
+    GtkActionGroup *std_actions, *preset_actions;
+
+    plugin_gui_window();
+    std::string make_gui_preset_list(GtkActionGroup *grp);
+    void fill_gui_presets();
+    void create(plugin_ctl_iface *_plugin, const char *title, const char *effect);
+    ~plugin_gui_window();
+};
+
 inline parameter_properties &param_control::get_props() 
 { 
     return  *gui->plugin->get_param_props(param_no);
diff --git a/src/calf/preset.h b/src/calf/preset.h
index e836c1f..c2aa766 100644
--- a/src/calf/preset.h
+++ b/src/calf/preset.h
@@ -53,6 +53,34 @@ struct plugin_preset
     }
 };
 
+struct preset_exception
+{
+    std::string message, param, fulltext;
+    int error;
+    preset_exception(const char *_message, const char *_param, int _error)
+    : message(_message), param(_param), error(_error)
+    {
+    }
+    const char *what() {
+        if (error)
+            fulltext = message + " " + param + " (" + strerror(error) + ")";
+        else
+            fulltext = message + " " + param;
+        return fulltext.c_str();
+    }
+    ~preset_exception()
+    {
+    }
+};
+
+extern std::string get_preset_filename();
+extern void load_presets(const char *filename);
+extern void save_presets(const char *filename);
+extern void add_preset(const plugin_preset &sp);
+
+// this global variable is a temporary measure
+extern std::vector<plugin_preset> presets;
+
 };
 
 #endif
diff --git a/src/calf/preset_gui.h b/src/calf/preset_gui.h
new file mode 100644
index 0000000..afd22f4
--- /dev/null
+++ b/src/calf/preset_gui.h
@@ -0,0 +1,44 @@
+/* Calf DSP Library
+ * GUI functions for 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 General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+#ifndef __CALF_PRESET_GUI_H
+#define __CALF_PRESET_GUI_H
+
+
+#include <calf/giface.h>
+#include <calf/gui.h>
+
+namespace synth {
+
+struct activate_preset_params
+{
+    plugin_gui *gui;
+    int preset;
+    
+    activate_preset_params(plugin_gui *_gui, int _preset)
+    : gui(_gui), preset(_preset)
+    {
+    }
+};
+
+void store_preset(GtkWindow *toplevel, plugin_gui *gui);
+void activate_preset(GtkAction *action, activate_preset_params *params);
+
+};
+
+#endif
diff --git a/src/calf/synth.h b/src/calf/synth.h
index 3c02c90..b256008 100644
--- a/src/calf/synth.h
+++ b/src/calf/synth.h
@@ -166,8 +166,8 @@ public:
     
     env_state state;
     // note: these are *rates*, not times
-    float attack, decay, sustain, release;
-    float value;
+    double attack, decay, sustain, release;
+    double value;
     
     adsr()
     {
@@ -233,6 +233,10 @@ public:
             break;
         case SUSTAIN:
             value = sustain;
+            if (value < 0.00001f) {
+                value = 0;
+                state = STOP;
+            }
             break;
         case RELEASE:
             value -= release;
diff --git a/src/gui.cpp b/src/gui.cpp
index 45136f7..c87ebc9 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -22,9 +22,11 @@
  
 #include <calf/giface.h>
 #include <calf/gui.h>
+#include <calf/preset.h>
+#include <calf/preset_gui.h>
 
 using namespace synth;
-
+using namespace std;
 
 /******************************** controls ********************************/
 
@@ -196,12 +198,16 @@ void knob_param_control::knob_value_changed(PhatKnob *widget, gpointer value)
 
 /******************************** GUI proper ********************************/
 
-plugin_gui::plugin_gui(GtkWidget *_toplevel)
-: toplevel(_toplevel)
+plugin_gui::plugin_gui(plugin_gui_window *_window)
+: window(_window)
 {
     
 }
 
+static void action_destroy_notify(gpointer data)
+{
+    delete (activate_preset_params *)data;
+}
 
 GtkWidget *plugin_gui::create(plugin_ctl_iface *_plugin, const char *title)
 {
@@ -267,3 +273,125 @@ void plugin_gui::refresh()
     }
 }
 
+/******************************* Actions **************************************************/
+ 
+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 },
+};
+
+/***************************** GUI window ********************************************/
+
+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"
+;
+
+plugin_gui_window::plugin_gui_window()
+{
+    toplevel = NULL;
+    ui_mgr = NULL;
+    std_actions = NULL;
+    preset_actions = NULL;
+}
+
+string plugin_gui_window::make_gui_preset_list(GtkActionGroup *grp)
+{
+    string preset_xml = preset_pre_xml;
+    for (unsigned int i = 0; i < presets.size(); i++)
+    {
+        if (presets[i].plugin != gui->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_full(preset_actions, &ae, 1, (gpointer)new activate_preset_params(gui, i), action_destroy_notify);
+    }
+    preset_xml += preset_post_xml;
+    return preset_xml;
+}
+
+void plugin_gui_window::fill_gui_presets()
+{
+    if(preset_actions) {
+        gtk_ui_manager_remove_action_group(ui_mgr, preset_actions);
+        preset_actions = NULL;
+    }
+    
+    preset_actions = gtk_action_group_new("presets");
+    string preset_xml = make_gui_preset_list(preset_actions);
+    gtk_ui_manager_insert_action_group(ui_mgr, preset_actions, 0);    
+    GError *error = NULL;
+    gtk_ui_manager_add_ui_from_string(ui_mgr, preset_xml.c_str(), -1, &error);
+}
+
+void plugin_gui_window::create(plugin_ctl_iface *_jh, const char *title, const char *effect)
+{
+    toplevel = GTK_WINDOW(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));
+
+    gui = new plugin_gui(this);
+    gui->effect_name = effect;
+
+    ui_mgr = gtk_ui_manager_new();
+    std_actions = gtk_action_group_new("default");
+    gtk_action_group_add_actions(std_actions, actions, sizeof(actions)/sizeof(actions[0]), this);
+    GError *error = NULL;
+    gtk_ui_manager_insert_action_group(ui_mgr, std_actions, 0);
+    gtk_ui_manager_add_ui_from_string(ui_mgr, ui_xml, -1, &error);    
+    fill_gui_presets();
+    
+    gtk_container_add(GTK_CONTAINER(vbox), gtk_ui_manager_get_widget(ui_mgr, "/ui/menubar"));
+    
+    GtkWidget *table = gui->create(_jh, effect);
+
+    gtk_container_add(GTK_CONTAINER(vbox), table);
+
+    gtk_widget_show_all(GTK_WIDGET(toplevel));
+    
+}
+
+plugin_gui_window::~plugin_gui_window()
+{
+    delete gui;
+}
diff --git a/src/jackhost.cpp b/src/jackhost.cpp
index 50a5631..fe70d3f 100644
--- a/src/jackhost.cpp
+++ b/src/jackhost.cpp
@@ -23,8 +23,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <config.h>
-#include <fcntl.h>
-#include <expat.h>
+#include <sys/stat.h>
 #include <glade/glade.h>
 #include <jack/jack.h>
 #include <calf/giface.h>
@@ -33,14 +32,13 @@
 #include <calf/modules_dev.h>
 #include <calf/gui.h>
 #include <calf/preset.h>
+#include <calf/preset_gui.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;
+plugin_gui_window *gui_win;
 
 const char *effect_name = "flanger";
 const char *client_name = "calfhost";
@@ -53,299 +51,6 @@ 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)
-{
-    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);
-    GtkWidget *table = gui->create(jh, effect);
-
-    gtk_container_add(GTK_CONTAINER(vbox), table);
-
-    gtk_signal_connect(GTK_OBJECT(toplevel), "destroy", G_CALLBACK(destroy), NULL);
-    gtk_widget_show_all(toplevel);
-    
-}
-
 static struct option long_options[] = {
     {"help", 0, 0, 'h'},
     {"version", 0, 0, 'v'},
@@ -396,7 +101,11 @@ int main(int argc, char *argv[])
         }
     }
     try {
-        load_presets();
+        struct stat st;
+        if (!stat(get_preset_filename().c_str(), &st))
+            load_presets(get_preset_filename().c_str());
+        else if (!stat(PKGLIBDIR "/presets.xml", &st))
+            load_presets(PKGLIBDIR "/presets.xml");
         jack_host_base *jh = NULL;
         if (!strcmp(effect_name, "reverb"))
             jh = new jack_host<reverb_audio_module>();
@@ -419,11 +128,13 @@ int main(int argc, char *argv[])
             return 1;
         }
         jh->open(client_name, input_name, output_name, midi_name);
-        make_gui(jh, client_name, effect_name);
+        gui_win = new plugin_gui_window;
+        gui_win->create(jh, client_name, effect_name);
+        gtk_signal_connect(GTK_OBJECT(gui_win->toplevel), "destroy", G_CALLBACK(destroy), NULL);
         gtk_main();
-        delete gui;
+        delete gui_win;
         jh->close();
-        save_presets();
+        save_presets(get_preset_filename().c_str());
         delete jh;
     }
     catch(std::exception &e)
diff --git a/src/preset.cpp b/src/preset.cpp
new file mode 100644
index 0000000..c1bbdce
--- /dev/null
+++ b/src/preset.cpp
@@ -0,0 +1,174 @@
+/* Calf DSP Library
+ * Preset management.
+ *
+ * Copyright (C) 2001-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 <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <expat.h>
+#include <calf/preset.h>
+
+using namespace synth;
+using namespace std;
+
+vector<plugin_preset> synth::presets;
+
+string synth::get_preset_filename()
+{
+    const char *home = getenv("HOME");
+    return string(home)+"/.calfpresets";
+}
+
+enum PresetParserState
+{
+    START,
+    LIST,
+    PRESET,
+    VALUE,
+};
+
+static PresetParserState xml_parser_state;
+static plugin_preset parser_preset;
+
+static 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);
+    throw preset_exception("Invalid XML element: %s", name, 0);
+}
+
+static 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;
+    }
+    throw preset_exception("Invalid XML element close: %s", name, 0);
+}
+
+void synth::load_presets(const char *filename)
+{
+    xml_parser_state = START;
+    XML_Parser parser = XML_ParserCreate("UTF-8");
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0) 
+        throw preset_exception("Could not load the presets from ", filename, errno);
+    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 synth::save_presets(const char *filename)
+{
+    string xml = "<presets>\n";
+    for (unsigned int i = 0; i < presets.size(); i++)
+    {
+        xml += presets[i].to_xml();
+    }
+    xml += "</presets>";
+    int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
+    if (fd < 0 || ((unsigned)write(fd, xml.c_str(), xml.length()) != xml.length()))
+        throw preset_exception("Could not save the presets in ", filename, errno);
+    close(fd);
+}
+
+void synth::add_preset(const plugin_preset &sp)
+{
+    presets.push_back(sp);
+}
diff --git a/src/preset_gui.cpp b/src/preset_gui.cpp
new file mode 100644
index 0000000..3736f62
--- /dev/null
+++ b/src/preset_gui.cpp
@@ -0,0 +1,97 @@
+/* Calf DSP Library
+ * GUI functions for 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 General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+ 
+#include <map>
+#include <glade/glade.h>
+#include <calf/giface.h>
+#include <calf/gui.h>
+#include <calf/preset.h>
+#include <calf/preset_gui.h>
+
+using namespace synth;
+using namespace std;
+
+// this way of filling presets menu is not that great
+
+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(GtkAction *action, plugin_gui *gui)
+{
+    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 = gui->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));
+    }
+    add_preset(sp);
+    gtk_widget_destroy(store_preset_dlg);
+    gui->window->fill_gui_presets();
+}
+
+void store_preset_cancel(GtkAction *action, plugin_gui *gui)
+{
+    gtk_widget_destroy(store_preset_dlg);
+}
+
+void synth::store_preset(GtkWindow *toplevel, plugin_gui *gui)
+{
+    if (store_preset_dlg)
+    {
+        gtk_window_present(GTK_WINDOW(store_preset_dlg));
+        return;
+    }
+    store_preset_xml = glade_xml_new(PKGLIBDIR "/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), gui);
+    gtk_signal_connect(GTK_OBJECT(glade_xml_get_widget(store_preset_xml, "cancel_button")), "clicked", G_CALLBACK(store_preset_cancel), gui);
+    gtk_window_set_transient_for(GTK_WINDOW(store_preset_dlg), toplevel);
+}
+
+void synth::activate_preset(GtkAction *action, activate_preset_params *params)
+{
+    plugin_gui *gui = params->gui;
+    plugin_preset &p = presets[params->preset];
+    if (p.plugin != gui->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();
+}
+

-- 
calf audio plugins packaging



More information about the pkg-multimedia-commits mailing list