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
25 #include <calf/giface.h>
27 #include <calf/main_win.h>
28 #include <calf/osctl.h>
29 #include <calf/osctlnet.h>
30 #include <calf/osctlserv.h>
34 using namespace calf_plugins
;
35 using namespace osctl
;
37 #define debug_printf(...)
42 string sdata
= string("\000\000\000\003123\000test\000\000\000\000\000\000\000\001\000\000\000\002", 24);
44 vector
<osc_data
> data
;
45 is
.read("bsii", data
);
46 assert(is
.pos
== sdata
.length());
47 assert(data
.size() == 4);
48 assert(data
[0].type
== osc_blob
);
49 assert(data
[1].type
== osc_string
);
50 assert(data
[2].type
== osc_i32
);
51 assert(data
[3].type
== osc_i32
);
52 assert(data
[0].strval
== "123");
53 assert(data
[1].strval
== "test");
54 assert(data
[2].i32val
== 1);
55 assert(data
[3].i32val
== 2);
58 assert(os
.buffer
== sdata
);
60 srv
.bind("0.0.0.0", 4541);
63 cli
.bind("0.0.0.0", 0);
64 cli
.set_addr("0.0.0.0", 4541);
65 if (!cli
.send("/blah", data
))
66 g_error("Could not send the OSC message");
68 g_main_loop_run(g_main_loop_new(NULL
, FALSE
));
74 enum { HAS_COLOR
= 1, HAS_WIDTH
= 2 };
90 struct graph_item
: public cairo_params
95 struct dot_item
: public cairo_params
101 struct gridline_item
: public cairo_params
108 struct param_line_graphs
110 vector
<graph_item
*> graphs
;
111 vector
<dot_item
*> dots
;
112 vector
<gridline_item
*> gridlines
;
117 void param_line_graphs::clear()
119 for (size_t i
= 0; i
< graphs
.size(); i
++)
123 for (size_t i
= 0; i
< dots
.size(); i
++)
127 for (size_t i
= 0; i
< gridlines
.size(); i
++)
133 struct plugin_proxy
: public plugin_ctl_iface
, public plugin_metadata_proxy
, public line_graph_iface
138 map
<string
, string
> cfg_vars
;
141 map
<int, param_line_graphs
> graphs
;
144 plugin_proxy(plugin_metadata_iface
*md
)
145 : plugin_metadata_proxy(md
)
149 update_graphs
= true;
151 param_count
= md
->get_param_count();
152 params
= new float[param_count
];
153 for (int i
= 0; i
< param_count
; i
++)
154 params
[i
] = get_param_props(i
)->def_value
;
156 virtual float get_param_value(int param_no
) {
157 if (param_no
< 0 || param_no
>= param_count
)
159 return params
[param_no
];
161 virtual void set_param_value(int param_no
, float value
) {
162 if (param_no
< 0 || param_no
>= param_count
)
164 update_graphs
= true;
165 params
[param_no
] = value
;
168 osc_inline_typed_strstream str
;
169 str
<< (uint32_t)(param_no
+ get_param_port_offset()) << value
;
170 client
->send("/control", str
);
173 virtual bool activate_preset(int bank
, int program
) {
175 osc_inline_typed_strstream str
;
176 str
<< (uint32_t)(bank
) << (uint32_t)(program
);
177 client
->send("/program", str
);
182 virtual float get_level(unsigned int port
) { return 0.f
; }
183 virtual void execute(int command_no
) {
188 osc_inline_typed_strstream str
;
189 str
<< "ExecCommand" << ss
.str();
190 client
->send("/configure", str
);
193 str
<< "ExecCommand" << "";
194 client
->send("/configure", str
);
197 char *configure(const char *key
, const char *value
) {
198 // do not store temporary vars in presets
199 if (strncmp(key
, "OSC:", 4))
200 cfg_vars
[key
] = value
;
201 osc_inline_typed_strstream str
;
203 client
->send("/configure", str
);
206 void send_configures(send_configure_iface
*sci
) {
207 for (map
<string
, string
>::iterator i
= cfg_vars
.begin(); i
!= cfg_vars
.end(); i
++)
208 sci
->send_configure(i
->first
.c_str(), i
->second
.c_str());
210 virtual line_graph_iface
*get_line_graph_iface() { return this; }
211 virtual bool get_graph(int index
, int subindex
, float *data
, int points
, cairo_iface
*context
);
212 virtual bool get_dot(int index
, int subindex
, float &x
, float &y
, int &size
, cairo_iface
*context
);
213 virtual bool get_gridline(int index
, int subindex
, float &pos
, bool &vertical
, std::string
&legend
, cairo_iface
*context
);
214 void update_cairo_context(cairo_iface
*context
, cairo_params
&item
);
217 bool plugin_proxy::get_graph(int index
, int subindex
, float *data
, int points
, cairo_iface
*context
)
219 if (!graphs
.count(index
))
221 param_line_graphs
&g
= graphs
[index
];
222 if (subindex
< (int)g
.graphs
.size())
224 float *sdata
= g
.graphs
[subindex
]->data
;
225 for (int i
= 0; i
< points
; i
++) {
226 float pos
= i
* 127.0 / points
;
227 int ipos
= i
* 127 / points
;
228 data
[i
] = sdata
[ipos
] + (sdata
[ipos
+ 1] - sdata
[ipos
]) * (pos
-ipos
);
230 update_cairo_context(context
, *g
.graphs
[subindex
]);
236 bool plugin_proxy::get_dot(int index
, int subindex
, float &x
, float &y
, int &size
, cairo_iface
*context
)
238 if (!graphs
.count(index
))
240 param_line_graphs
&g
= graphs
[index
];
241 if (subindex
< (int)g
.dots
.size())
243 dot_item
&item
= *g
.dots
[subindex
];
247 update_cairo_context(context
, item
);
253 bool plugin_proxy::get_gridline(int index
, int subindex
, float &pos
, bool &vertical
, std::string
&legend
, cairo_iface
*context
)
255 if (!graphs
.count(index
))
257 param_line_graphs
&g
= graphs
[index
];
258 if (subindex
< (int)g
.gridlines
.size())
260 gridline_item
&item
= *g
.gridlines
[subindex
];
262 vertical
= item
.vertical
!= 0;
264 update_cairo_context(context
, item
);
270 void plugin_proxy::update_cairo_context(cairo_iface
*context
, cairo_params
&item
)
272 if (item
.flags
& cairo_params::HAS_COLOR
)
273 context
->set_source_rgba(item
.r
, item
.g
, item
.b
, item
.a
);
274 if (item
.flags
& cairo_params::HAS_WIDTH
)
275 context
->set_line_width(item
.line_width
);
278 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
282 static bool osc_debug
= false;
284 struct dssi_osc_server
: public osc_server
, public osc_message_sink
<osc_strstream
>
286 plugin_proxy
*plugin
;
287 main_window
*main_win
;
288 plugin_gui_window
*window
;
289 string effect_name
, title
;
291 bool in_program
, enable_dump
;
292 vector
<plugin_preset
> presets
;
293 /// Timeout callback source ID
295 bool osc_link_active
;
299 , main_win(new main_window
)
300 , window(new plugin_gui_window(main_win
))
304 osc_link_active
= false;
307 static void on_destroy(GtkWindow
*window
, dssi_osc_server
*self
)
309 debug_printf("on_destroy, have to send \"exiting\"\n");
310 bool result
= self
->cli
.send("/exiting");
311 debug_printf("result = %d\n", result
? 1 : 0);
312 g_main_loop_quit(mainloop
);
313 // eliminate a warning with empty debug_printf
320 vector
<plugin_metadata_iface
*> plugins
;
321 get_all_plugins(plugins
);
322 for (unsigned int i
= 0; i
< plugins
.size(); i
++)
324 if (!strcmp(plugins
[i
]->get_id(), effect_name
.c_str()))
326 plugin
= new plugin_proxy(plugins
[i
]);
332 fprintf(stderr
, "Unknown plugin: %s\n", effect_name
.c_str());
335 plugin
->client
= &cli
;
336 plugin
->send_osc
= true;
337 ((main_window
*)window
->main
)->conditions
.insert("dssi");
338 ((main_window
*)window
->main
)->conditions
.insert("directlink");
339 window
->create(plugin
, title
.c_str(), effect_name
.c_str());
340 plugin
->gui
= window
->gui
;
341 gtk_signal_connect(GTK_OBJECT(window
->toplevel
), "destroy", G_CALLBACK(on_destroy
), this);
342 vector
<plugin_preset
> tmp_presets
;
343 get_builtin_presets().get_for_plugin(presets
, effect_name
.c_str());
344 get_user_presets().get_for_plugin(tmp_presets
, effect_name
.c_str());
345 presets
.insert(presets
.end(), tmp_presets
.begin(), tmp_presets
.end());
346 source_id
= g_timeout_add_full(G_PRIORITY_LOW
, 1000/30, on_idle
, this, NULL
);
349 static gboolean
on_idle(void *self
)
351 dssi_osc_server
*self_ptr
= (dssi_osc_server
*)(self
);
352 if (self_ptr
->osc_link_active
)
353 self_ptr
->send_osc_update();
357 void set_osc_update(bool enabled
);
358 void send_osc_update();
360 virtual void receive_osc_message(std::string address
, std::string args
, osc_strstream
&buffer
);
361 void unmarshal_line_graph(osc_strstream
&buffer
);
364 void dssi_osc_server::set_osc_update(bool enabled
)
366 osc_link_active
= enabled
;
367 osc_inline_typed_strstream data
;
368 data
<< "OSC:FEEDBACK_URI";
369 data
<< (enabled
? get_uri() : "");
370 cli
.send("/configure", data
);
373 void dssi_osc_server::send_osc_update()
375 static int serial_no
= 0;
376 osc_inline_typed_strstream data
;
377 data
<< "OSC:UPDATE";
378 data
<< calf_utils::i2s(serial_no
++);
379 cli
.send("/configure", data
);
382 void dssi_osc_server::unmarshal_line_graph(osc_strstream
&buffer
)
388 if (cmd
== LGI_GRAPH
)
392 param_line_graphs
&graphs
= plugin
->graphs
[param
];
399 if (cmd
== LGI_SET_RGBA
)
401 params
.flags
|= cairo_params::HAS_COLOR
;
402 buffer
>> params
.r
>> params
.g
>> params
.b
>> params
.a
;
405 if (cmd
== LGI_SET_WIDTH
)
407 params
.flags
|= cairo_params::HAS_WIDTH
;
408 buffer
>> params
.line_width
;
411 if (cmd
== LGI_SUBGRAPH
)
413 buffer
>> param
; // ignore number of points
414 graph_item
*gi
= new graph_item
;
415 (cairo_params
&)*gi
= params
;
416 for (int i
= 0; i
< 128; i
++)
417 buffer
>> gi
->data
[i
];
418 graphs
.graphs
.push_back(gi
);
424 dot_item
*di
= new dot_item
;
425 (cairo_params
&)*di
= params
;
426 buffer
>> di
->x
>> di
->y
>> di
->size
;
427 graphs
.dots
.push_back(di
);
431 if (cmd
== LGI_LEGEND
)
433 gridline_item
*li
= new gridline_item
;
434 (cairo_params
&)*li
= params
;
435 buffer
>> li
->pos
>> li
->vertical
>> li
->text
;
436 graphs
.gridlines
.push_back(li
);
449 void dssi_osc_server::receive_osc_message(std::string address
, std::string args
, osc_strstream
&buffer
)
452 dump
.receive_osc_message(address
, args
, buffer
);
453 if (address
== prefix
+ "/update" && args
== "s")
457 debug_printf("UPDATE: %s\n", str
.c_str());
458 set_osc_update(true);
462 else if (address
== prefix
+ "/quit")
464 set_osc_update(false);
465 debug_printf("QUIT\n");
466 g_main_loop_quit(mainloop
);
469 else if (address
== prefix
+ "/configure"&& args
== "ss")
472 buffer
>> key
>> value
;
473 // do not store temporary vars in presets
474 if (strncmp(key
.c_str(), "OSC:", 4))
475 plugin
->cfg_vars
[key
] = value
;
476 // XXXKF perhaps this should be queued !
477 window
->gui
->refresh();
480 else if (address
== prefix
+ "/program"&& args
== "ii")
482 uint32_t bank
, program
;
484 buffer
>> bank
>> program
;
486 unsigned int nr
= bank
* 128 + program
;
487 debug_printf("PROGRAM %d\n", nr
);
490 bool sosc
= plugin
->send_osc
;
491 plugin
->send_osc
= false;
492 int count
= plugin
->get_param_count();
493 for (int i
=0 ; i
< count
; i
++)
494 plugin
->set_param_value(i
, plugin
->get_param_props(i
)->def_value
);
495 plugin
->send_osc
= sosc
;
496 window
->gui
->refresh();
497 // special handling for default preset
501 if (nr
>= presets
.size())
503 bool sosc
= plugin
->send_osc
;
504 plugin
->send_osc
= false;
505 presets
[nr
].activate(plugin
);
506 plugin
->send_osc
= sosc
;
507 window
->gui
->refresh();
509 // cli.send("/update", data);
512 else if (address
== prefix
+ "/control" && args
== "if")
517 buffer
>> port
>> val
;
519 int idx
= port
- plugin
->get_param_port_offset();
520 debug_printf("CONTROL %d %f\n", idx
, val
);
521 bool sosc
= plugin
->send_osc
;
522 plugin
->send_osc
= false;
523 window
->gui
->set_param_value(idx
, val
);
524 plugin
->send_osc
= sosc
;
525 if (plugin
->get_param_props(idx
)->flags
& PF_PROP_GRAPH
)
526 plugin
->update_graphs
= true;
529 else if (address
== prefix
+ "/show")
531 set_osc_update(true);
533 gtk_widget_show_all(GTK_WIDGET(window
->toplevel
));
536 else if (address
== prefix
+ "/hide")
538 set_osc_update(false);
540 gtk_widget_hide(GTK_WIDGET(window
->toplevel
));
543 else if (address
== prefix
+ "/lineGraph")
545 unmarshal_line_graph(buffer
);
546 if (plugin
->update_graphs
) {
547 // updates graphs that are only redrawn on startup and parameter changes
548 // (the OSC message may come a while after the parameter has been changed,
549 // so the redraw triggered by parameter change usually shows stale values)
550 window
->gui
->refresh();
551 plugin
->update_graphs
= false;
556 printf("Unknown OSC address: %s\n", address
.c_str());
559 //////////////////////////////////////////////////////////////////////////////////
561 void help(char *argv
[])
563 printf("GTK+ user interface for Calf DSSI plugins\nSyntax: %s [--help] [--version] <osc-url> <so-file> <plugin-label> <instance-name>\n", argv
[0]);
566 static struct option long_options
[] = {
568 {"version", 0, 0, 'v'},
569 {"debug", 0, 0, 'd'},
573 int main(int argc
, char *argv
[])
575 char *debug_var
= getenv("OSC_DEBUG");
576 if (debug_var
&& atoi(debug_var
))
579 gtk_init(&argc
, &argv
);
582 int c
= getopt_long(argc
, argv
, "dhv", long_options
, &option_index
);
593 printf("%s\n", PACKAGE_STRING
);
597 if (argc
- optind
!= 4)
604 get_builtin_presets().load_defaults(true);
605 get_user_presets().load_defaults(false);
607 catch(calf_plugins::preset_exception
&e
)
609 fprintf(stderr
, "Error while loading presets: %s\n", e
.what());
614 srv
.prefix
= "/dssi/"+string(argv
[optind
+ 1]) + "/" + string(argv
[optind
+ 2]);
615 for (char *p
= argv
[optind
+ 2]; *p
; p
++)
617 srv
.effect_name
= argv
[optind
+ 2];
618 srv
.title
= argv
[optind
+ 3];
623 mainloop
= g_main_loop_new(NULL
, FALSE
);
626 srv
.cli
.set_url(argv
[optind
]);
628 debug_printf("URI = %s\n", srv
.get_uri().c_str());
630 string data_buf
, type_buf
;
631 osc_inline_typed_strstream data
;
632 data
<< srv
.get_uri();
633 if (!srv
.cli
.send("/update", data
))
635 g_error("Could not send the initial update message via OSC to %s", argv
[optind
]);
639 g_main_loop_run(mainloop
);
641 g_source_remove(srv
.source_id
);
643 srv
.set_osc_update(false);
644 debug_printf("exited\n");