+ DSP primitives: fix a rather stupid bug in clamping functions
[calf.git] / src / lv2gui.cpp
blob6dce0c1e475be89841998440867cb0e1df3e8098
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 <assert.h>
21 #include <getopt.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <config.h>
25 #include <calf/giface.h>
26 #include <calf/gui.h>
27 #include <calf/main_win.h>
28 #include <calf/lv2_data_access.h>
29 #include <calf/lv2_string_port.h>
30 #include <calf/lv2_ui.h>
31 #include <calf/lv2_uri_map.h>
32 #include <calf/preset_gui.h>
33 #include <calf/utils.h>
34 #include <calf/lv2helpers.h>
36 using namespace std;
37 using namespace dsp;
38 using namespace calf_plugins;
39 using namespace calf_utils;
41 struct LV2_Calf_Descriptor {
42 plugin_ctl_iface *(*get_pci)(LV2_Handle Instance);
45 struct plugin_proxy: public plugin_ctl_iface, public plugin_metadata_proxy
47 LV2UI_Write_Function write_function;
48 LV2UI_Controller controller;
50 bool send;
51 plugin_gui *gui;
52 float *params;
53 int param_count;
54 /// Instance pointer - usually NULL unless the host supports instance-access extension
55 plugin_ctl_iface *instance;
56 int source_id;
57 LV2_Handle instance_handle;
58 LV2_Extension_Data_Feature *data_access;
59 LV2_URI_Map_Feature *uri_map;
60 map<string, int> params_by_name;
61 uint32_t string_port_uri;
63 plugin_proxy(plugin_metadata_iface *md)
64 : plugin_metadata_proxy(md)
66 gui = NULL;
67 instance = NULL;
68 instance_handle = NULL;
69 data_access = NULL;
70 send = true;
71 param_count = get_param_count();
72 params = new float[param_count];
73 string_port_uri = 0;
74 for (int i = 0; i < param_count; i++)
76 parameter_properties *pp = get_param_props(i);
77 params_by_name[pp->short_name] = i;
78 if ((pp->flags & PF_TYPEMASK) < PF_STRING)
79 params[i] = pp->def_value;
83 void setup(LV2UI_Write_Function wfn, LV2UI_Controller ctl)
85 write_function = wfn;
86 controller = ctl;
89 virtual float get_param_value(int param_no) {
90 if (param_no < 0 || param_no >= param_count)
91 return 0;
92 return params[param_no];
95 virtual void set_param_value(int param_no, float value) {
96 if (param_no < 0 || param_no >= param_count)
97 return;
98 if ((get_param_props(param_no)->flags & PF_TYPEMASK) >= PF_STRING)
100 //assert(0);
101 return;
103 params[param_no] = value;
104 if (send) {
105 scope_assign<bool> _a_(send, false);
106 write_function(controller, param_no + get_param_port_offset(), sizeof(float), 0, &params[param_no]);
110 virtual bool activate_preset(int bank, int program)
112 return false;
115 virtual line_graph_iface *get_line_graph_iface() {
116 if (instance)
117 return instance->get_line_graph_iface();
118 return NULL;
121 virtual char *configure(const char *key, const char *value)
123 if (!send)
124 return NULL;
125 map<string, int>::iterator i = params_by_name.find(key);
126 if (i == params_by_name.end())
128 fprintf(stderr, "ERROR: configure called for unknown key %s\n", key);
129 assert(0);
130 return NULL;
132 LV2_String_Data data;
133 data.data = (char *)value;
134 data.len = strlen(value);
135 data.storage = -1; // host doesn't need that
136 data.flags = 0;
137 data.pad = 0;
139 int idx = i->second;
140 if (string_port_uri) {
141 write_function(controller, idx + get_param_port_offset(), sizeof(LV2_String_Data), string_port_uri, &data);
144 return NULL;
147 virtual float get_level(unsigned int port) { return 0.f; }
148 virtual void execute(int command_no) { assert(0); }
149 void send_configures(send_configure_iface *sci) {
150 fprintf(stderr, "TODO: send_configures (non-control port configuration dump) not implemented in LV2 GUIs\n");
152 void 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);
162 uint32_t map_uri(const char *mapURI, const char *keyURI)
164 if (!uri_map)
165 return 0;
166 return uri_map->uri_to_id(uri_map->callback_data, mapURI, keyURI);
169 ~plugin_proxy()
171 delete []params;
175 static gboolean plugin_on_idle(void *data)
177 plugin_gui *self = (plugin_gui *)data;
178 self->on_idle();
179 return TRUE;
182 LV2UI_Handle gui_instantiate(const struct _LV2UI_Descriptor* descriptor,
183 const char* plugin_uri,
184 const char* bundle_path,
185 LV2UI_Write_Function write_function,
186 LV2UI_Controller controller,
187 LV2UI_Widget* widget,
188 const LV2_Feature* const* features)
190 vector<plugin_metadata_iface *> plugins;
191 get_all_plugins(plugins);
192 const char *label = plugin_uri + sizeof("http://calf.sourceforge.net/plugins/") - 1;
193 plugin_proxy *proxy = NULL;
194 for (unsigned int i = 0; i < plugins.size(); i++)
196 if (!strcmp(plugins[i]->get_plugin_info().label, label))
198 proxy = new plugin_proxy(plugins[i]);
199 break;
202 if (!proxy)
203 return NULL;
204 for (int i = 0; features[i]; i++)
206 if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/instance-access"))
207 proxy->instance_handle = features[i]->data;
208 else if (!strcmp(features[i]->URI, "http://lv2plug.in/ns/ext/data-access"))
209 proxy->data_access = (LV2_Extension_Data_Feature *)features[i]->data;
210 else if (!strcmp(features[i]->URI, LV2_URI_MAP_URI))
212 proxy->uri_map = (LV2_URI_Map_Feature *)features[i]->data;
213 proxy->string_port_uri = proxy->map_uri("http://lv2plug.in/ns/extensions/ui",
214 LV2_STRING_PORT_URI);
217 proxy->resolve_instance();
218 scope_assign<bool> _a_(proxy->send, false);
219 proxy->setup(write_function, controller);
220 // dummy window
221 main_window *main = new main_window;
222 if (proxy->instance)
223 main->conditions.insert("directlink");
224 main->conditions.insert("lv2gui");
225 plugin_gui_window *window = new plugin_gui_window(main);
226 plugin_gui *gui = new plugin_gui(window);
227 const char *xml = proxy->get_gui_xml();
228 assert(xml);
229 *(GtkWidget **)(widget) = gui->create_from_xml(proxy, xml);
231 if (*(GtkWidget **)(widget))
232 proxy->source_id = g_timeout_add_full(G_PRIORITY_LOW, 1000/30, plugin_on_idle, gui, NULL); // 30 fps should be enough for everybody
234 return (LV2UI_Handle)gui;
237 void gui_cleanup(LV2UI_Handle handle)
239 plugin_gui *gui = (plugin_gui *)handle;
240 plugin_proxy *proxy = dynamic_cast<plugin_proxy *>(gui->plugin);
241 if (proxy->source_id)
242 g_source_remove(proxy->source_id);
243 delete gui;
246 void gui_port_event(LV2UI_Handle handle, uint32_t port, uint32_t buffer_size, uint32_t format, const void *buffer)
248 plugin_gui *gui = (plugin_gui *)handle;
249 plugin_proxy *proxy = dynamic_cast<plugin_proxy *>(gui->plugin);
250 assert(proxy);
251 float v = *(float *)buffer;
252 port -= gui->plugin->get_param_port_offset();
253 if (port >= (uint32_t)gui->plugin->get_param_count())
254 return;
255 if ((gui->plugin->get_param_props(port)->flags & PF_TYPEMASK) == PF_STRING)
257 scope_assign<bool> _a_(proxy->send, false);
258 gui->plugin->configure(gui->plugin->get_param_props(port)->short_name, ((LV2_String_Data *)buffer)->data);
259 return;
261 if (fabs(gui->plugin->get_param_value(port) - v) < 0.00001)
262 return;
264 scope_assign<bool> _a_(proxy->send, false);
265 gui->set_param_value(port, v);
269 const void *gui_extension(const char *uri)
271 return NULL;
274 namespace calf_plugins {
276 // this function is normally implemented in preset_gui.cpp, but we're not using that file
277 void activate_preset(GtkAction *action, activate_preset_params *params)
281 // so is this function
282 void store_preset(GtkWindow *toplevel, plugin_gui *gui)
288 ///////////////////////////////////////////////////////////////////////////////////////
290 class small_plugin_gui: public uri_map_access
292 public:
293 LV2UI_Write_Function write_function;
294 LV2UI_Controller controller;
295 GtkWidget **widget_ptr;
297 void write(int port, const void *data, uint32_t size, uint32_t format)
299 (*write_function)(controller, port, size, format, &data);
302 virtual void use_feature(const char *URI, void *feature) {
305 virtual void parse_features(const LV2_Feature* const*features) {
306 if (features) {
307 for(;*features;features++)
308 use_feature((*features)->URI, (*features)->data);
312 virtual GtkWidget *create_widget()=0;
314 virtual void init(const char* plugin_uri, const char* bundle_path,
315 LV2UI_Write_Function write_function, LV2UI_Controller controller,
316 LV2UI_Widget* widget, const LV2_Feature* const*features)
318 this->write_function = write_function;
319 this->controller = controller;
320 widget_ptr = (GtkWidget **)widget;
321 parse_features(features);
322 *widget_ptr = create_widget();
325 virtual ~small_plugin_gui() {}
328 class msg_read_gui: public message_mixin<small_plugin_gui>
330 GtkWidget *editor;
331 uint32_t set_float_msg, float_type;
333 static void bang(GtkWidget *widget, msg_read_gui *gui)
335 struct payload {
336 uint32_t selector;
337 uint32_t serial_no;
338 uint32_t data_size;
339 uint32_t parg_count;
340 uint32_t data_type;
341 float data_value;
342 uint32_t narg_count;
343 } data;
344 data.selector = gui->set_float_msg;
345 data.serial_no = 1337;
346 data.data_size = 16;
347 data.parg_count = 1;
348 data.data_type = gui->float_type;
349 data.data_value = atof(gtk_entry_get_text(GTK_ENTRY(gui->editor)));
350 data.narg_count = 0;
351 gui->write(0, &data, sizeof(data), gui->message_event_type);
353 virtual void map_uris()
355 message_mixin<small_plugin_gui>::map_uris();
356 set_float_msg = map_uri("http://lv2plug.in/ns/dev/msg", "http://foltman.com/garbage/setFloat");
357 float_type = map_uri("http://lv2plug.in/ns/dev/types", "http://lv2plug.in/ns/dev/types#float");
359 virtual GtkWidget *create_widget()
361 editor = gtk_entry_new();
362 GtkWidget *button = gtk_button_new_with_label("Bang!");
363 GtkWidget *vbox = gtk_vbox_new(false, 10);
364 gtk_box_pack_start(GTK_BOX(vbox), editor, true, true, 5);
365 gtk_box_pack_start(GTK_BOX(vbox), button, true, true, 5);
366 GtkWidget *frame = gtk_frame_new("GUI");
367 gtk_container_add(GTK_CONTAINER(frame), vbox);
368 gtk_widget_queue_resize(frame);
369 gtk_signal_connect( GTK_OBJECT(button), "clicked", G_CALLBACK(bang), this);
370 return frame;
374 LV2UI_Handle sgui_instantiate(const struct _LV2UI_Descriptor* descriptor,
375 const char* plugin_uri,
376 const char* bundle_path,
377 LV2UI_Write_Function write_function,
378 LV2UI_Controller controller,
379 LV2UI_Widget* widget,
380 const LV2_Feature* const* features)
382 small_plugin_gui *gui = NULL;
383 if (!strcmp(plugin_uri, "http://calf.sourceforge.net/small_plugins/msgread_e"))
384 gui = new msg_read_gui;
385 else
386 return NULL;
387 gui->init(plugin_uri, bundle_path, write_function, controller, widget, features);
388 return gui;
391 void sgui_cleanup(LV2UI_Handle handle)
393 small_plugin_gui *gui = (small_plugin_gui *)handle;
394 delete gui;
397 void sgui_port_event(LV2UI_Handle handle, uint32_t port, uint32_t buffer_size, uint32_t format, const void *buffer)
401 const void *sgui_extension(const char *uri)
403 return NULL;
406 ///////////////////////////////////////////////////////////////////////////////////////
408 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
410 static LV2UI_Descriptor gui, sgui;
411 gui.URI = "http://calf.sourceforge.net/plugins/gui/gtk2-gui";
412 gui.instantiate = gui_instantiate;
413 gui.cleanup = gui_cleanup;
414 gui.port_event = gui_port_event;
415 gui.extension_data = gui_extension;
416 sgui.URI = "http://calf.sourceforge.net/small_plugins/gui/gtk2-gui";
417 sgui.instantiate = sgui_instantiate;
418 sgui.cleanup = sgui_cleanup;
419 sgui.port_event = sgui_port_event;
420 sgui.extension_data = sgui_extension;
421 switch(index) {
422 case 0:
423 return &gui;
424 case 1:
425 return &sgui;
426 default:
427 return NULL;