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
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
;
108 instance_handle
= 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
;
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, ¶ms
[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
)
167 return urid_map
->map(urid_map
->handle
, uri
);
170 const line_graph_iface
*plugin_proxy_base::get_line_graph_iface() const
173 return instance
->get_line_graph_iface();
177 const phase_graph_iface
*plugin_proxy_base::get_phase_graph_iface() const
180 return instance
->get_phase_graph_iface();
184 char *plugin_proxy_base::configure(const char *key
, const char *value
)
187 return instance
->configure(key
, value
);
189 return strdup("Configuration not available because of lack of instance-access/data-access");
192 void plugin_proxy_base::enable_all_sends()
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
206 /// Glib source ID for update timer
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
)
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
)
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
)
230 send_float_to_host(param_no
, value
);
233 virtual bool activate_preset(int bank
, int program
)
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
)
247 fprintf(stderr
, "Send configures...\n");
248 instance
->send_configures(sci
);
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
)
256 return instance
->send_status_updates(sui
, last_serial
);
257 else // no status updates because of lack of instance-access/data-access
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
;
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
)
284 gtk_init(&argc
, NULL
);
286 const plugin_metadata_iface
*md
= plugin_registry::instance().get_by_uri(plugin_uri
);
289 lv2_plugin_proxy
*proxy
= new lv2_plugin_proxy(md
, write_function
, controller
, features
);
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();
299 gui
->optwidget
= gui
->create_from_xml(proxy
, xml
);
300 proxy
->enable_all_sends();
301 proxy
->send_configures(gui
);
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
;
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
);
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
);
348 gtk_widget_destroy(gui
->optwindow
);
350 free((void*)gui
->opttitle
);
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
);
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())
363 if (!proxy
->sends
[param
])
365 if (fabs(gui
->plugin
->get_param_value(param
) - v
) < 0.00001)
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
);
391 gtk_container_add(GTK_CONTAINER(gui
->optwindow
), gui
->optwidget
);
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
));
405 int gui_hide(LV2UI_Handle handle
)
407 plugin_gui
*gui
= (plugin_gui
*)handle
;
410 gtk_widget_hide_all(gui
->optwindow
);
415 int gui_idle(LV2UI_Handle handle
)
417 plugin_gui
*gui
= (plugin_gui
*)handle
;
423 gtk_main_iteration();
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
))
434 if (!strcmp(uri
, LV2_UI__idleInterface
))
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
;