Button: emit slopes (fix for stuck modulaor plug-ins)
[calf.git] / src / lv2gui.cpp
blob9d5f8e71fb6e74d67020951dd426ea38416047c8
1 /* Calf DSP Library utility application.
2 * DSSI GUI application.
3 * Copyright (C) 2007 Krzysztof Foltman
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
20 #include <sys/wait.h>
21 #include "config.h"
22 #include <calf/gui.h>
23 #include <calf/giface.h>
24 #include <calf/lv2_data_access.h>
25 #include <calf/lv2_ui.h>
26 #include <calf/lv2_uri_map.h>
27 #include <calf/lv2_external_ui.h>
28 #include <calf/lv2helpers.h>
29 #include <calf/utils.h>
30 #include <glib.h>
32 using namespace std;
33 using namespace calf_plugins;
34 using namespace calf_utils;
36 struct LV2_Calf_Descriptor {
37 plugin_ctl_iface *(*get_pci)(LV2_Handle Instance);
40 /// Temporary assignment to a slot in vector<bool>
41 typedef scope_assign<bool, vector<bool>::reference> TempSendSetter;
43 /// Common data and functions for GTK+ GUI and External GUI
44 struct plugin_proxy_base
46 const plugin_metadata_iface *plugin_metadata;
47 LV2UI_Write_Function write_function;
48 LV2UI_Controller controller;
50 // Values extracted from the Features array from the host
52 /// Handle to the plugin instance
53 LV2_Handle instance_handle;
54 /// Data access feature instance
55 LV2_Extension_Data_Feature *data_access;
56 /// URI map feature
57 LV2_URI_Map_Feature *uri_map;
58 /// External UI host feature (must be set when instantiating external UI plugins)
59 lv2_external_ui_host *ext_ui_host;
61 /// Instance pointer - usually NULL unless the host supports instance-access extension
62 plugin_ctl_iface *instance;
63 /// If true, a given parameter (not port) may be sent to host - it is blocked when the parameter is written to by the host
64 vector<bool> sends;
65 /// Map of parameter name to parameter index (used for mapping configure values to string ports)
66 map<string, int> params_by_name;
67 /// Values of parameters (float control ports)
68 vector<float> params;
69 /// Number of parameters (non-audio ports)
70 int param_count;
71 /// Number of the first parameter port
72 int param_offset;
74 plugin_proxy_base(const plugin_metadata_iface *metadata, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* features);
76 /// Send a float value to a control port in the host
77 void send_float_to_host(int param_no, float value);
79 /// Send a string value to a string port in the host, by name (configure-like mechanism)
80 char *configure(const char *key, const char *value);
82 /// Enable sending to host for all ports
83 void enable_all_sends();
85 /// Obtain instance pointers
86 void resolve_instance();
88 /// Obtain line graph interface if available
89 const line_graph_iface *get_line_graph_iface() const;
91 /// Obtain phase graph interface if available
92 const phase_graph_iface *get_phase_graph_iface() const;
94 /// Map an URI to an integer value using a given URI map
95 uint32_t map_uri(const char *mapURI, const char *keyURI);
99 plugin_proxy_base::plugin_proxy_base(const plugin_metadata_iface *metadata, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* features)
101 plugin_metadata = metadata;
103 write_function = wf;
104 controller = c;
106 instance = NULL;
107 instance_handle = NULL;
108 data_access = NULL;
109 ext_ui_host = NULL;
111 param_count = metadata->get_param_count();
112 param_offset = metadata->get_param_port_offset();
114 /// Block all updates until GUI is ready
115 sends.resize(param_count, false);
116 params.resize(param_count);
117 for (int i = 0; i < param_count; i++)
119 const parameter_properties *pp = metadata->get_param_props(i);
120 params_by_name[pp->short_name] = i;
121 params[i] = pp->def_value;
123 for (int i = 0; features[i]; i++)
125 if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/instance-access"))
127 instance_handle = features[i]->data;
129 else if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/data-access"))
131 data_access = (LV2_Extension_Data_Feature *)features[i]->data;
133 else if (!strcmp(features[i]->URI, LV2_EXTERNAL_UI_URI))
135 ext_ui_host = (lv2_external_ui_host *)features[i]->data;
138 resolve_instance();
141 void plugin_proxy_base::send_float_to_host(int param_no, float value)
143 params[param_no] = value;
144 if (sends[param_no]) {
145 TempSendSetter _a_(sends[param_no], false);
146 write_function(controller, param_no + param_offset, sizeof(float), 0, &params[param_no]);
150 void plugin_proxy_base::resolve_instance()
152 fprintf(stderr, "CALF DEBUG: instance %p data %p\n", instance_handle, data_access);
153 if (instance_handle && data_access)
155 LV2_Calf_Descriptor *calf = (LV2_Calf_Descriptor *)(*data_access->data_access)("http://foltman.com/ns/calf-plugin-instance");
156 fprintf(stderr, "CALF DEBUG: calf %p cpi %p\n", calf, calf ? calf->get_pci : NULL);
157 if (calf && calf->get_pci)
158 instance = calf->get_pci(instance_handle);
162 uint32_t plugin_proxy_base::map_uri(const char *mapURI, const char *keyURI)
164 if (!uri_map)
165 return 0;
166 return uri_map->uri_to_id(uri_map->callback_data, mapURI, keyURI);
169 const line_graph_iface *plugin_proxy_base::get_line_graph_iface() const
171 if (instance)
172 return instance->get_line_graph_iface();
173 return NULL;
176 const phase_graph_iface *plugin_proxy_base::get_phase_graph_iface() const
178 if (instance)
179 return instance->get_phase_graph_iface();
180 return NULL;
183 char *plugin_proxy_base::configure(const char *key, const char *value)
185 if (instance)
186 return instance->configure(key, value);
187 else
188 return strdup("Configuration not available because of lack of instance-access/data-access");
191 void plugin_proxy_base::enable_all_sends()
193 sends.clear();
194 sends.resize(param_count, true);
197 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
199 /// Plugin controller that uses LV2 host with help of instance/data access to remotely
200 /// control a plugin from the GUI
201 struct lv2_plugin_proxy: public plugin_ctl_iface, public plugin_proxy_base, public gui_environment
203 /// Plugin GTK+ GUI object pointer
204 plugin_gui *gui;
205 /// Glib source ID for update timer
206 int source_id;
208 lv2_plugin_proxy(const plugin_metadata_iface *md, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* f)
209 : plugin_proxy_base(md, wf, c, f)
211 gui = NULL;
212 if (instance)
214 conditions.insert("directlink");
215 conditions.insert("configure");
217 conditions.insert("lv2gui");
220 virtual float get_param_value(int param_no) {
221 if (param_no < 0 || param_no >= param_count)
222 return 0;
223 return params[param_no];
226 virtual void set_param_value(int param_no, float value) {
227 if (param_no < 0 || param_no >= param_count)
228 return;
229 send_float_to_host(param_no, value);
232 virtual bool activate_preset(int bank, int program)
234 return false;
237 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
238 virtual char *configure(const char *key, const char *value) { return plugin_proxy_base::configure(key, value); }
240 virtual float get_level(unsigned int port) { return 0.f; }
241 virtual void execute(int command_no) { assert(0); }
242 virtual void send_configures(send_configure_iface *sci)
244 if (instance)
246 fprintf(stderr, "Send configures...\n");
247 instance->send_configures(sci);
249 else
250 fprintf(stderr, "Configuration not available because of lack of instance-access/data-access\n");
252 virtual int send_status_updates(send_updates_iface *sui, int last_serial)
254 if (instance)
255 return instance->send_status_updates(sui, last_serial);
256 else // no status updates because of lack of instance-access/data-access
257 return 0;
259 virtual const plugin_metadata_iface *get_metadata_iface() const { return plugin_metadata; }
260 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
261 virtual const line_graph_iface *get_line_graph_iface() const { return plugin_proxy_base::get_line_graph_iface(); }
263 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
264 virtual const phase_graph_iface *get_phase_graph_iface() const { return plugin_proxy_base::get_phase_graph_iface(); }
267 static gboolean plugin_on_idle(void *data)
269 plugin_gui *self = (plugin_gui *)data;
270 self->on_idle();
271 return TRUE;
274 LV2UI_Handle gui_instantiate(const struct _LV2UI_Descriptor* descriptor,
275 const char* plugin_uri,
276 const char* bundle_path,
277 LV2UI_Write_Function write_function,
278 LV2UI_Controller controller,
279 LV2UI_Widget* widget,
280 const LV2_Feature* const* features)
282 const plugin_metadata_iface *md = plugin_registry::instance().get_by_uri(plugin_uri);
283 if (!md)
284 return NULL;
285 lv2_plugin_proxy *proxy = new lv2_plugin_proxy(md, write_function, controller, features);
286 if (!proxy)
287 return NULL;
289 gtk_rc_parse(PKGLIBDIR "calf.rc");
291 plugin_gui_window *window = new plugin_gui_window(proxy, NULL);
292 plugin_gui *gui = new plugin_gui(window);
293 const char *xml = proxy->plugin_metadata->get_gui_xml();
294 assert(xml);
295 *(GtkWidget **)(widget) = gui->create_from_xml(proxy, xml);
296 proxy->enable_all_sends();
297 proxy->send_configures(gui);
298 if (*(GtkWidget **)(widget))
299 proxy->source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, plugin_on_idle, gui, NULL); // 30 fps should be enough for everybody
300 gui->show_rack_ears(proxy->get_config()->rack_ears);
302 return (LV2UI_Handle)gui;
305 void gui_cleanup(LV2UI_Handle handle)
307 plugin_gui *gui = (plugin_gui *)handle;
308 lv2_plugin_proxy *proxy = dynamic_cast<lv2_plugin_proxy *>(gui->plugin);
309 if (proxy->source_id)
310 g_source_remove(proxy->source_id);
311 delete gui;
314 void gui_port_event(LV2UI_Handle handle, uint32_t port, uint32_t buffer_size, uint32_t format, const void *buffer)
316 plugin_gui *gui = (plugin_gui *)handle;
317 lv2_plugin_proxy *proxy = dynamic_cast<lv2_plugin_proxy *>(gui->plugin);
318 assert(proxy);
319 float v = *(float *)buffer;
320 int param = port - proxy->plugin_metadata->get_param_port_offset();
321 if (param >= proxy->plugin_metadata->get_param_count())
322 return;
323 if (!proxy->sends[param])
324 return;
325 if (fabs(gui->plugin->get_param_value(param) - v) < 0.00001)
326 return;
328 TempSendSetter _a_(proxy->sends[param], false);
329 gui->set_param_value(param, v);
333 const void *gui_extension(const char *uri)
335 return NULL;
338 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
340 static LV2UI_Descriptor gtkgui;
341 gtkgui.URI = "http://calf.sourceforge.net/plugins/gui/gtk2-gui";
342 gtkgui.instantiate = gui_instantiate;
343 gtkgui.cleanup = gui_cleanup;
344 gtkgui.port_event = gui_port_event;
345 gtkgui.extension_data = gui_extension;
346 if (!index--)
347 return &gtkgui;
349 return NULL;