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_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>
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
;
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
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)
69 /// Number of parameters (non-audio ports)
71 /// Number of the first parameter port
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
;
107 instance_handle
= 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
;
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, ¶ms
[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
)
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
172 return instance
->get_line_graph_iface();
176 const phase_graph_iface
*plugin_proxy_base::get_phase_graph_iface() const
179 return instance
->get_phase_graph_iface();
183 char *plugin_proxy_base::configure(const char *key
, const char *value
)
186 return instance
->configure(key
, value
);
188 return strdup("Configuration not available because of lack of instance-access/data-access");
191 void plugin_proxy_base::enable_all_sends()
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
205 /// Glib source ID for update timer
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
)
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
)
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
)
229 send_float_to_host(param_no
, value
);
232 virtual bool activate_preset(int bank
, int program
)
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
)
246 fprintf(stderr
, "Send configures...\n");
247 instance
->send_configures(sci
);
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
)
255 return instance
->send_status_updates(sui
, last_serial
);
256 else // no status updates because of lack of instance-access/data-access
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
;
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
);
285 lv2_plugin_proxy
*proxy
= new lv2_plugin_proxy(md
, write_function
, controller
, features
);
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();
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
);
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
);
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())
323 if (!proxy
->sends
[param
])
325 if (fabs(gui
->plugin
->get_param_value(param
) - v
) < 0.00001)
328 TempSendSetter
_a_(proxy
->sends
[param
], false);
329 gui
->set_param_value(param
, v
);
333 const void *gui_extension(const char *uri
)
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
;