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
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>
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
;
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
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)
70 /// Number of parameters (non-audio ports)
72 /// Number of the first parameter port
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
;
110 instance_handle
= 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
;
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, ¶ms
[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
)
170 return urid_map
->map(urid_map
->handle
, uri
);
173 const line_graph_iface
*plugin_proxy_base::get_line_graph_iface() const
176 return instance
->get_line_graph_iface();
180 const phase_graph_iface
*plugin_proxy_base::get_phase_graph_iface() const
183 return instance
->get_phase_graph_iface();
187 char *plugin_proxy_base::configure(const char *key
, const char *value
)
190 return instance
->configure(key
, value
);
192 return strdup("Configuration not available because of lack of instance-access/data-access");
195 void plugin_proxy_base::enable_all_sends()
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
209 /// Glib source ID for update timer
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
)
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
)
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
)
233 send_float_to_host(param_no
, value
);
236 virtual bool activate_preset(int bank
, int program
)
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
)
250 fprintf(stderr
, "Send configures...\n");
251 instance
->send_configures(sci
);
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
)
259 return instance
->send_status_updates(sui
, last_serial
);
260 else // no status updates because of lack of instance-access/data-access
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
) {
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
)
299 gtk_init(&argc
, NULL
);
301 const plugin_metadata_iface
*md
= plugin_registry::instance().get_by_uri(plugin_uri
);
304 lv2_plugin_proxy
*proxy
= new lv2_plugin_proxy(md
, write_function
, controller
, features
);
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();
314 gui
->optwidget
= gui
->create_from_xml(proxy
, xml
);
315 proxy
->enable_all_sends();
316 proxy
->send_configures(gui
);
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
;
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
);
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
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
;
374 gtk_widget_destroy(gui
->optwindow
);
376 free((void*)gui
->opttitle
);
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
;
385 lv2_plugin_proxy
*proxy
= dynamic_cast<lv2_plugin_proxy
*>(gui
->plugin
);
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())
391 if (!proxy
->sends
[param
])
393 if (fabs(gui
->plugin
->get_param_value(param
) - v
) < 0.00001)
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
);
419 gtk_container_add(GTK_CONTAINER(gui
->optwindow
), gui
->optwidget
);
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
));
433 int gui_hide(LV2UI_Handle handle
)
435 plugin_gui
*gui
= (plugin_gui
*)handle
;
438 gtk_widget_hide_all(gui
->optwindow
);
443 int gui_idle(LV2UI_Handle handle
)
445 plugin_gui
*gui
= (plugin_gui
*)handle
;
451 gtk_main_iteration();
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
))
462 if (!strcmp(uri
, LV2_UI__idleInterface
))
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
;