Little speed improvement
[calf.git] / src / lv2gui.cpp
blob100329867f5bbe33c7618732168b0b8932c0e465
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_options.h>
26 #include <calf/lv2_ui.h>
27 #include <calf/lv2_urid.h>
28 #include <calf/lv2_external_ui.h>
29 #include <calf/lv2helpers.h>
30 #include <calf/utils.h>
31 #include <glib.h>
33 using namespace std;
34 using namespace calf_plugins;
35 using namespace calf_utils;
37 struct LV2_Calf_Descriptor {
38 plugin_ctl_iface *(*get_pci)(LV2_Handle Instance);
41 /// Temporary assignment to a slot in vector<bool>
42 typedef scope_assign<bool, vector<bool>::reference> TempSendSetter;
44 /// Common data and functions for GTK+ GUI and External GUI
45 struct plugin_proxy_base
47 const plugin_metadata_iface *plugin_metadata;
48 LV2UI_Write_Function write_function;
49 LV2UI_Controller controller;
51 // Values extracted from the Features array from the host
53 /// Handle to the plugin instance
54 LV2_Handle instance_handle;
55 /// Data access feature instance
56 LV2_Extension_Data_Feature *data_access;
57 /// URID map feature
58 LV2_URID_Map *urid_map;
59 /// External UI host feature (must be set when instantiating external UI plugins)
60 lv2_external_ui_host *ext_ui_host;
62 /// Instance pointer - usually NULL unless the host supports instance-access extension
63 plugin_ctl_iface *instance;
64 /// If true, a given parameter (not port) may be sent to host - it is blocked when the parameter is written to by the host
65 vector<bool> sends;
66 /// Map of parameter name to parameter index (used for mapping configure values to string ports)
67 map<string, int> params_by_name;
68 /// Values of parameters (float control ports)
69 vector<float> params;
70 /// Number of parameters (non-audio ports)
71 int param_count;
72 /// Number of the first parameter port
73 int param_offset;
74 /// Signal handler for main widget destroyed
75 gulong widget_destroyed_signal;
77 plugin_proxy_base(const plugin_metadata_iface *metadata, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* features);
79 /// Send a float value to a control port in the host
80 void send_float_to_host(int param_no, float value);
82 /// Send a string value to a string port in the host, by name (configure-like mechanism)
83 char *configure(const char *key, const char *value);
85 /// Enable sending to host for all ports
86 void enable_all_sends();
88 /// Obtain instance pointers
89 void resolve_instance();
91 /// Obtain line graph interface if available
92 const line_graph_iface *get_line_graph_iface() const;
94 /// Obtain phase graph interface if available
95 const phase_graph_iface *get_phase_graph_iface() const;
97 /// Map an URI to an integer value using a given URID map
98 uint32_t map_urid(const char *uri);
102 plugin_proxy_base::plugin_proxy_base(const plugin_metadata_iface *metadata, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* features)
104 plugin_metadata = metadata;
106 write_function = wf;
107 controller = c;
109 instance = NULL;
110 instance_handle = NULL;
111 data_access = NULL;
112 ext_ui_host = NULL;
114 param_count = metadata->get_param_count();
115 param_offset = metadata->get_param_port_offset();
116 widget_destroyed_signal = 0;
118 /// Block all updates until GUI is ready
119 sends.resize(param_count, false);
120 params.resize(param_count);
121 for (int i = 0; i < param_count; i++)
123 const parameter_properties *pp = metadata->get_param_props(i);
124 params_by_name[pp->short_name] = i;
125 params[i] = pp->def_value;
127 for (int i = 0; features[i]; i++)
129 if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/instance-access"))
131 instance_handle = features[i]->data;
133 else if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/data-access"))
135 data_access = (LV2_Extension_Data_Feature *)features[i]->data;
137 else if (!strcmp(features[i]->URI, LV2_EXTERNAL_UI_URI))
139 ext_ui_host = (lv2_external_ui_host *)features[i]->data;
142 resolve_instance();
145 void plugin_proxy_base::send_float_to_host(int param_no, float value)
147 params[param_no] = value;
148 if (sends[param_no]) {
149 TempSendSetter _a_(sends[param_no], false);
150 write_function(controller, param_no + param_offset, sizeof(float), 0, &params[param_no]);
154 void plugin_proxy_base::resolve_instance()
156 fprintf(stderr, "CALF DEBUG: instance %p data %p\n", instance_handle, data_access);
157 if (instance_handle && data_access)
159 LV2_Calf_Descriptor *calf = (LV2_Calf_Descriptor *)(*data_access->data_access)("http://foltman.com/ns/calf-plugin-instance");
160 fprintf(stderr, "CALF DEBUG: calf %p cpi %p\n", calf, calf ? calf->get_pci : NULL);
161 if (calf && calf->get_pci)
162 instance = calf->get_pci(instance_handle);
166 uint32_t plugin_proxy_base::map_urid(const char *uri)
168 if (!urid_map)
169 return 0;
170 return urid_map->map(urid_map->handle, uri);
173 const line_graph_iface *plugin_proxy_base::get_line_graph_iface() const
175 if (instance)
176 return instance->get_line_graph_iface();
177 return NULL;
180 const phase_graph_iface *plugin_proxy_base::get_phase_graph_iface() const
182 if (instance)
183 return instance->get_phase_graph_iface();
184 return NULL;
187 char *plugin_proxy_base::configure(const char *key, const char *value)
189 if (instance)
190 return instance->configure(key, value);
191 else
192 return strdup("Configuration not available because of lack of instance-access/data-access");
195 void plugin_proxy_base::enable_all_sends()
197 sends.clear();
198 sends.resize(param_count, true);
201 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
203 /// Plugin controller that uses LV2 host with help of instance/data access to remotely
204 /// control a plugin from the GUI
205 struct lv2_plugin_proxy: public plugin_ctl_iface, public plugin_proxy_base, public gui_environment
207 /// Plugin GTK+ GUI object pointer
208 plugin_gui *gui;
209 /// Glib source ID for update timer
210 int source_id;
212 lv2_plugin_proxy(const plugin_metadata_iface *md, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* f)
213 : plugin_proxy_base(md, wf, c, f)
215 gui = NULL;
216 if (instance)
218 conditions.insert("directlink");
219 conditions.insert("configure");
221 conditions.insert("lv2gui");
224 virtual float get_param_value(int param_no) {
225 if (param_no < 0 || param_no >= param_count)
226 return 0;
227 return params[param_no];
230 virtual void set_param_value(int param_no, float value) {
231 if (param_no < 0 || param_no >= param_count)
232 return;
233 send_float_to_host(param_no, value);
236 virtual bool activate_preset(int bank, int program)
238 return false;
241 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
242 virtual char *configure(const char *key, const char *value) { return plugin_proxy_base::configure(key, value); }
244 virtual float get_level(unsigned int port) { return 0.f; }
245 virtual void execute(int command_no) { assert(0); }
246 virtual void send_configures(send_configure_iface *sci)
248 if (instance)
250 fprintf(stderr, "Send configures...\n");
251 instance->send_configures(sci);
253 else
254 fprintf(stderr, "Configuration not available because of lack of instance-access/data-access\n");
256 virtual int send_status_updates(send_updates_iface *sui, int last_serial)
258 if (instance)
259 return instance->send_status_updates(sui, last_serial);
260 else // no status updates because of lack of instance-access/data-access
261 return 0;
263 virtual const plugin_metadata_iface *get_metadata_iface() const { return plugin_metadata; }
264 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
265 virtual const line_graph_iface *get_line_graph_iface() const { return plugin_proxy_base::get_line_graph_iface(); }
267 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
268 virtual const phase_graph_iface *get_phase_graph_iface() const { return plugin_proxy_base::get_phase_graph_iface(); }
271 static gboolean plugin_on_idle(void *data)
273 plugin_gui *self = (plugin_gui *)data;
274 if (self->optwidget) {
275 self->on_idle();
276 return TRUE;
277 } else {
278 return FALSE;
282 static void on_gui_widget_destroy(GtkWidget*, gpointer data)
284 plugin_gui *gui = (plugin_gui *)data;
285 // Remove the reference to the widget, so that it's not being manipulated
286 // after it's been destroyed
287 gui->optwidget = NULL;
290 LV2UI_Handle gui_instantiate(const struct _LV2UI_Descriptor* descriptor,
291 const char* plugin_uri,
292 const char* bundle_path,
293 LV2UI_Write_Function write_function,
294 LV2UI_Controller controller,
295 LV2UI_Widget* widget,
296 const LV2_Feature* const* features)
298 static int argc = 0;
299 gtk_init(&argc, NULL);
301 const plugin_metadata_iface *md = plugin_registry::instance().get_by_uri(plugin_uri);
302 if (!md)
303 return NULL;
304 lv2_plugin_proxy *proxy = new lv2_plugin_proxy(md, write_function, controller, features);
305 if (!proxy)
306 return NULL;
308 gtk_rc_parse(PKGLIBDIR "calf.rc");
310 plugin_gui_window *window = new plugin_gui_window(proxy, NULL);
311 plugin_gui *gui = new plugin_gui(window);
312 const char *xml = proxy->plugin_metadata->get_gui_xml();
313 assert(xml);
314 gui->optwidget = gui->create_from_xml(proxy, xml);
315 proxy->enable_all_sends();
316 proxy->send_configures(gui);
317 if (gui->optwidget)
319 proxy->source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, plugin_on_idle, gui, NULL); // 30 fps should be enough for everybody
320 proxy->widget_destroyed_signal = g_signal_connect(G_OBJECT(gui->optwidget), "destroy", G_CALLBACK(on_gui_widget_destroy), (gpointer)gui);
322 gui->show_rack_ears(proxy->get_config()->rack_ears);
324 *(GtkWidget **)(widget) = gui->optwidget;
326 // find window title
327 const LV2_Options_Option* options = NULL;
328 const LV2_URID_Map* uridMap = NULL;
330 for (int i=0; features[i]; i++)
332 if (!strcmp(features[i]->URI, LV2_OPTIONS__options))
333 options = (const LV2_Options_Option*)features[i]->data;
334 else if (!strcmp(features[i]->URI, LV2_URID_MAP_URI))
335 uridMap = (const LV2_URID_Map*)features[i]->data;
338 if (!options || !uridMap)
339 return (LV2UI_Handle)gui;
341 const uint32_t uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
343 if (!uridWindowTitle)
344 return (LV2UI_Handle)gui;
346 for (int i=0; options[i].key; i++)
348 if (options[i].key == uridWindowTitle)
350 const char* windowTitle = (const char*)options[i].value;
351 gui->opttitle = strdup(windowTitle);
352 break;
356 return (LV2UI_Handle)gui;
359 void gui_cleanup(LV2UI_Handle handle)
361 plugin_gui *gui = (plugin_gui *)handle;
362 lv2_plugin_proxy *proxy = dynamic_cast<lv2_plugin_proxy *>(gui->plugin);
363 if (proxy->source_id)
364 g_source_remove(proxy->source_id);
365 // If the widget still exists, remove the handler
366 if (gui->optwidget)
368 g_signal_handler_disconnect(gui->optwidget, proxy->widget_destroyed_signal);
369 proxy->widget_destroyed_signal = 0;
371 gui->destroy_child_widgets(gui->optwidget);
372 gui->optwidget = NULL;
373 if (gui->optwindow)
374 gtk_widget_destroy(gui->optwindow);
375 if (gui->opttitle)
376 free((void*)gui->opttitle);
377 delete gui;
380 void gui_port_event(LV2UI_Handle handle, uint32_t port, uint32_t buffer_size, uint32_t format, const void *buffer)
382 plugin_gui *gui = (plugin_gui *)handle;
383 if (gui->optclosed)
384 return;
385 lv2_plugin_proxy *proxy = dynamic_cast<lv2_plugin_proxy *>(gui->plugin);
386 assert(proxy);
387 float v = *(float *)buffer;
388 int param = port - proxy->plugin_metadata->get_param_port_offset();
389 if (param < 0 || param >= proxy->plugin_metadata->get_param_count())
390 return;
391 if (!proxy->sends[param])
392 return;
393 if (fabs(gui->plugin->get_param_value(param) - v) < 0.00001)
394 return;
396 TempSendSetter _a_(proxy->sends[param], false);
397 gui->set_param_value(param, v);
401 void gui_destroy(GtkWidget*, gpointer data)
403 plugin_gui *gui = (plugin_gui *)data;
405 gui->optclosed = true;
406 gui->optwindow = NULL;
409 int gui_show(LV2UI_Handle handle)
411 plugin_gui *gui = (plugin_gui *)handle;
413 if (! gui->optwindow)
415 gui->optwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
416 g_signal_connect(G_OBJECT(gui->optwindow), "destroy", G_CALLBACK(gui_destroy), (gpointer)gui);
418 if (gui->optwidget)
419 gtk_container_add(GTK_CONTAINER(gui->optwindow), gui->optwidget);
421 if (gui->opttitle)
422 gtk_window_set_title(GTK_WINDOW(gui->optwindow), gui->opttitle);
424 gtk_window_set_resizable(GTK_WINDOW(gui->optwindow), false);
427 gtk_widget_show_all(gui->optwindow);
428 gtk_window_present(GTK_WINDOW(gui->optwindow));
430 return 0;
433 int gui_hide(LV2UI_Handle handle)
435 plugin_gui *gui = (plugin_gui *)handle;
437 if (gui->optwindow)
438 gtk_widget_hide_all(gui->optwindow);
440 return 0;
443 int gui_idle(LV2UI_Handle handle)
445 plugin_gui *gui = (plugin_gui *)handle;
447 if (gui->optclosed)
448 return 1;
450 if (gui->optwindow)
451 gtk_main_iteration();
453 return 0;
456 const void *gui_extension(const char *uri)
458 static const LV2UI_Show_Interface show = { gui_show, gui_hide };
459 static const LV2UI_Idle_Interface idle = { gui_idle };
460 if (!strcmp(uri, LV2_UI__showInterface))
461 return &show;
462 if (!strcmp(uri, LV2_UI__idleInterface))
463 return &idle;
464 return NULL;
467 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
469 static LV2UI_Descriptor gtkgui;
470 gtkgui.URI = "http://calf.sourceforge.net/plugins/gui/gtk2-gui";
471 gtkgui.instantiate = gui_instantiate;
472 gtkgui.cleanup = gui_cleanup;
473 gtkgui.port_event = gui_port_event;
474 gtkgui.extension_data = gui_extension;
475 if (!index--)
476 return &gtkgui;
478 return NULL;