Fix bypass crossfade read/write out of bounds
[calf.git] / src / lv2gui.cpp
blobeb092a68c24d29d4d6455b058622ac6f968f8d9a
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;
75 plugin_proxy_base(const plugin_metadata_iface *metadata, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* features);
77 /// Send a float value to a control port in the host
78 void send_float_to_host(int param_no, float value);
80 /// Send a string value to a string port in the host, by name (configure-like mechanism)
81 char *configure(const char *key, const char *value);
83 /// Enable sending to host for all ports
84 void enable_all_sends();
86 /// Obtain instance pointers
87 void resolve_instance();
89 /// Obtain line graph interface if available
90 const line_graph_iface *get_line_graph_iface() const;
92 /// Obtain phase graph interface if available
93 const phase_graph_iface *get_phase_graph_iface() const;
95 /// Map an URI to an integer value using a given URID map
96 uint32_t map_urid(const char *uri);
100 plugin_proxy_base::plugin_proxy_base(const plugin_metadata_iface *metadata, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* features)
102 plugin_metadata = metadata;
104 write_function = wf;
105 controller = c;
107 instance = NULL;
108 instance_handle = NULL;
109 data_access = NULL;
110 ext_ui_host = NULL;
112 param_count = metadata->get_param_count();
113 param_offset = metadata->get_param_port_offset();
115 /// Block all updates until GUI is ready
116 sends.resize(param_count, false);
117 params.resize(param_count);
118 for (int i = 0; i < param_count; i++)
120 const parameter_properties *pp = metadata->get_param_props(i);
121 params_by_name[pp->short_name] = i;
122 params[i] = pp->def_value;
124 for (int i = 0; features[i]; i++)
126 if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/instance-access"))
128 instance_handle = features[i]->data;
130 else if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/data-access"))
132 data_access = (LV2_Extension_Data_Feature *)features[i]->data;
134 else if (!strcmp(features[i]->URI, LV2_EXTERNAL_UI_URI))
136 ext_ui_host = (lv2_external_ui_host *)features[i]->data;
139 resolve_instance();
142 void plugin_proxy_base::send_float_to_host(int param_no, float value)
144 params[param_no] = value;
145 if (sends[param_no]) {
146 TempSendSetter _a_(sends[param_no], false);
147 write_function(controller, param_no + param_offset, sizeof(float), 0, &params[param_no]);
151 void plugin_proxy_base::resolve_instance()
153 fprintf(stderr, "CALF DEBUG: instance %p data %p\n", instance_handle, data_access);
154 if (instance_handle && data_access)
156 LV2_Calf_Descriptor *calf = (LV2_Calf_Descriptor *)(*data_access->data_access)("http://foltman.com/ns/calf-plugin-instance");
157 fprintf(stderr, "CALF DEBUG: calf %p cpi %p\n", calf, calf ? calf->get_pci : NULL);
158 if (calf && calf->get_pci)
159 instance = calf->get_pci(instance_handle);
163 uint32_t plugin_proxy_base::map_urid(const char *uri)
165 if (!urid_map)
166 return 0;
167 return urid_map->map(urid_map->handle, uri);
170 const line_graph_iface *plugin_proxy_base::get_line_graph_iface() const
172 if (instance)
173 return instance->get_line_graph_iface();
174 return NULL;
177 const phase_graph_iface *plugin_proxy_base::get_phase_graph_iface() const
179 if (instance)
180 return instance->get_phase_graph_iface();
181 return NULL;
184 char *plugin_proxy_base::configure(const char *key, const char *value)
186 if (instance)
187 return instance->configure(key, value);
188 else
189 return strdup("Configuration not available because of lack of instance-access/data-access");
192 void plugin_proxy_base::enable_all_sends()
194 sends.clear();
195 sends.resize(param_count, true);
198 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
200 /// Plugin controller that uses LV2 host with help of instance/data access to remotely
201 /// control a plugin from the GUI
202 struct lv2_plugin_proxy: public plugin_ctl_iface, public plugin_proxy_base, public gui_environment
204 /// Plugin GTK+ GUI object pointer
205 plugin_gui *gui;
206 /// Glib source ID for update timer
207 int source_id;
209 lv2_plugin_proxy(const plugin_metadata_iface *md, LV2UI_Write_Function wf, LV2UI_Controller c, const LV2_Feature* const* f)
210 : plugin_proxy_base(md, wf, c, f)
212 gui = NULL;
213 if (instance)
215 conditions.insert("directlink");
216 conditions.insert("configure");
218 conditions.insert("lv2gui");
221 virtual float get_param_value(int param_no) {
222 if (param_no < 0 || param_no >= param_count)
223 return 0;
224 return params[param_no];
227 virtual void set_param_value(int param_no, float value) {
228 if (param_no < 0 || param_no >= param_count)
229 return;
230 send_float_to_host(param_no, value);
233 virtual bool activate_preset(int bank, int program)
235 return false;
238 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
239 virtual char *configure(const char *key, const char *value) { return plugin_proxy_base::configure(key, value); }
241 virtual float get_level(unsigned int port) { return 0.f; }
242 virtual void execute(int command_no) { assert(0); }
243 virtual void send_configures(send_configure_iface *sci)
245 if (instance)
247 fprintf(stderr, "Send configures...\n");
248 instance->send_configures(sci);
250 else
251 fprintf(stderr, "Configuration not available because of lack of instance-access/data-access\n");
253 virtual int send_status_updates(send_updates_iface *sui, int last_serial)
255 if (instance)
256 return instance->send_status_updates(sui, last_serial);
257 else // no status updates because of lack of instance-access/data-access
258 return 0;
260 virtual const plugin_metadata_iface *get_metadata_iface() const { return plugin_metadata; }
261 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
262 virtual const line_graph_iface *get_line_graph_iface() const { return plugin_proxy_base::get_line_graph_iface(); }
264 /// Override for a method in plugin_ctl_iface - trivial delegation to base class
265 virtual const phase_graph_iface *get_phase_graph_iface() const { return plugin_proxy_base::get_phase_graph_iface(); }
268 static gboolean plugin_on_idle(void *data)
270 plugin_gui *self = (plugin_gui *)data;
271 self->on_idle();
272 return TRUE;
275 LV2UI_Handle gui_instantiate(const struct _LV2UI_Descriptor* descriptor,
276 const char* plugin_uri,
277 const char* bundle_path,
278 LV2UI_Write_Function write_function,
279 LV2UI_Controller controller,
280 LV2UI_Widget* widget,
281 const LV2_Feature* const* features)
283 static int argc = 0;
284 gtk_init(&argc, NULL);
286 const plugin_metadata_iface *md = plugin_registry::instance().get_by_uri(plugin_uri);
287 if (!md)
288 return NULL;
289 lv2_plugin_proxy *proxy = new lv2_plugin_proxy(md, write_function, controller, features);
290 if (!proxy)
291 return NULL;
293 gtk_rc_parse(PKGLIBDIR "calf.rc");
295 plugin_gui_window *window = new plugin_gui_window(proxy, NULL);
296 plugin_gui *gui = new plugin_gui(window);
297 const char *xml = proxy->plugin_metadata->get_gui_xml();
298 assert(xml);
299 gui->optwidget = gui->create_from_xml(proxy, xml);
300 proxy->enable_all_sends();
301 proxy->send_configures(gui);
302 if (gui->optwidget)
303 proxy->source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, plugin_on_idle, gui, NULL); // 30 fps should be enough for everybody
304 gui->show_rack_ears(proxy->get_config()->rack_ears);
306 *(GtkWidget **)(widget) = gui->optwidget;
308 // find window title
309 const LV2_Options_Option* options = NULL;
310 const LV2_URID_Map* uridMap = NULL;
312 for (int i=0; features[i]; i++)
314 if (!strcmp(features[i]->URI, LV2_OPTIONS__options))
315 options = (const LV2_Options_Option*)features[i]->data;
316 else if (!strcmp(features[i]->URI, LV2_URID_MAP_URI))
317 uridMap = (const LV2_URID_Map*)features[i]->data;
320 if (!options || !uridMap)
321 return (LV2UI_Handle)gui;
323 const uint32_t uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
325 if (!uridWindowTitle)
326 return (LV2UI_Handle)gui;
328 for (int i=0; options[i].key; i++)
330 if (options[i].key == uridWindowTitle)
332 const char* windowTitle = (const char*)options[i].value;
333 gui->opttitle = strdup(windowTitle);
334 break;
338 return (LV2UI_Handle)gui;
341 void gui_cleanup(LV2UI_Handle handle)
343 plugin_gui *gui = (plugin_gui *)handle;
344 lv2_plugin_proxy *proxy = dynamic_cast<lv2_plugin_proxy *>(gui->plugin);
345 if (proxy->source_id)
346 g_source_remove(proxy->source_id);
347 if (gui->optwindow)
348 gtk_widget_destroy(gui->optwindow);
349 if (gui->opttitle)
350 free((void*)gui->opttitle);
351 delete gui;
354 void gui_port_event(LV2UI_Handle handle, uint32_t port, uint32_t buffer_size, uint32_t format, const void *buffer)
356 plugin_gui *gui = (plugin_gui *)handle;
357 lv2_plugin_proxy *proxy = dynamic_cast<lv2_plugin_proxy *>(gui->plugin);
358 assert(proxy);
359 float v = *(float *)buffer;
360 int param = port - proxy->plugin_metadata->get_param_port_offset();
361 if (param >= proxy->plugin_metadata->get_param_count())
362 return;
363 if (!proxy->sends[param])
364 return;
365 if (fabs(gui->plugin->get_param_value(param) - v) < 0.00001)
366 return;
368 TempSendSetter _a_(proxy->sends[param], false);
369 gui->set_param_value(param, v);
373 void gui_destroy(GtkWidget*, gpointer data)
375 plugin_gui *gui = (plugin_gui *)data;
377 gui->optclosed = true;
378 gui->optwindow = NULL;
381 int gui_show(LV2UI_Handle handle)
383 plugin_gui *gui = (plugin_gui *)handle;
385 if (! gui->optwindow)
387 gui->optwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
388 g_signal_connect(G_OBJECT(gui->optwindow), "destroy", G_CALLBACK(gui_destroy), (gpointer)gui);
390 if (gui->optwidget)
391 gtk_container_add(GTK_CONTAINER(gui->optwindow), gui->optwidget);
393 if (gui->opttitle)
394 gtk_window_set_title(GTK_WINDOW(gui->optwindow), gui->opttitle);
396 gtk_window_set_resizable(GTK_WINDOW(gui->optwindow), false);
399 gtk_widget_show_all(gui->optwindow);
400 gtk_window_present(GTK_WINDOW(gui->optwindow));
402 return 0;
405 int gui_hide(LV2UI_Handle handle)
407 plugin_gui *gui = (plugin_gui *)handle;
409 if (gui->optwindow)
410 gtk_widget_hide_all(gui->optwindow);
412 return 0;
415 int gui_idle(LV2UI_Handle handle)
417 plugin_gui *gui = (plugin_gui *)handle;
419 if (gui->optclosed)
420 return 1;
422 if (gui->optwindow)
423 gtk_main_iteration();
425 return 0;
428 const void *gui_extension(const char *uri)
430 static const LV2UI_Show_Interface show = { gui_show, gui_hide };
431 static const LV2UI_Idle_Interface idle = { gui_idle };
432 if (!strcmp(uri, LV2_UI__showInterface))
433 return &show;
434 if (!strcmp(uri, LV2_UI__idleInterface))
435 return &idle;
436 return NULL;
439 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
441 static LV2UI_Descriptor gtkgui;
442 gtkgui.URI = "http://calf.sourceforge.net/plugins/gui/gtk2-gui";
443 gtkgui.instantiate = gui_instantiate;
444 gtkgui.cleanup = gui_cleanup;
445 gtkgui.port_event = gui_port_event;
446 gtkgui.extension_data = gui_extension;
447 if (!index--)
448 return &gtkgui;
450 return NULL;