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