+ Filterclavier: unbreak calfjackhost display of filter graph
[calf.git] / src / jackhost.cpp
blobbd970cec861de26680a4fea21f8075aa9938ff8d
1 /* Calf DSP Library Utility Application - calfjackhost
2 * Standalone application module wrapper example.
4 * Copyright (C) 2007 Krzysztof Foltman
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * 02110-1301, USA.
21 #include <set>
22 #include <getopt.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <config.h>
26 #include <glade/glade.h>
27 #include <jack/jack.h>
28 #if USE_LASH
29 #include <lash/lash.h>
30 #endif
31 #include <calf/giface.h>
32 #include <calf/jackhost.h>
33 #include <calf/modules.h>
34 #include <calf/modules_dev.h>
35 #include <calf/gui.h>
36 #include <calf/preset.h>
37 #include <calf/preset_gui.h>
38 #include <calf/main_win.h>
39 #include <calf/utils.h>
41 using namespace std;
42 using namespace calf_utils;
43 using namespace calf_plugins;
45 // I don't need anyone to tell me this is stupid. I already know that :)
46 plugin_gui_window *gui_win;
48 const char *client_name = "calfhost";
50 jack_host_base *calf_plugins::create_jack_host(const char *effect_name, const std::string &instance_name, calf_plugins::progress_report_iface *priface)
52 #define PER_MODULE_ITEM(name, isSynth, jackname) if (!strcasecmp(effect_name, jackname)) return new jack_host<name##_audio_module>(effect_name, instance_name, priface);
53 #include <calf/modulelist.h>
54 return NULL;
57 void jack_host_base::open(jack_client *_client)
59 client = _client; //jack_client_open(client_name, JackNullOption, &status);
61 create_ports();
63 cache_ports();
65 init_module();
66 changed = false;
69 void jack_host_base::create_ports() {
70 char buf[32];
71 char buf2[64];
72 string prefix = client->name + ":";
73 static const char *suffixes[] = { "l", "r", "2l", "2r" };
74 port *inputs = get_inputs();
75 port *outputs = get_outputs();
76 int in_count = get_input_count(), out_count = get_output_count();
77 for (int i=0; i<in_count; i++) {
78 sprintf(buf, "%s_in_%s", instance_name.c_str(), suffixes[i]);
79 sprintf(buf2, client->input_name.c_str(), client->input_nr++);
80 inputs[i].name = buf2;
81 inputs[i].handle = jack_port_register(client->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput , 0);
82 inputs[i].data = NULL;
83 if (!inputs[i].handle)
84 throw text_exception("Could not create JACK input port");
85 jack_port_set_alias(inputs[i].handle, (prefix + buf2).c_str());
87 if (get_midi()) {
88 sprintf(buf, "%s_midi_in", instance_name.c_str());
89 sprintf(buf2, client->midi_name.c_str(), client->midi_nr++);
90 midi_port.name = buf2;
91 midi_port.handle = jack_port_register(client->client, buf, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
92 if (!midi_port.handle)
93 throw text_exception("Could not create JACK MIDI port");
94 jack_port_set_alias(midi_port.handle, (prefix + buf2).c_str());
96 for (int i=0; i<out_count; i++) {
97 sprintf(buf, "%s_out_%s", instance_name.c_str(), suffixes[i]);
98 sprintf(buf2, client->output_name.c_str(), client->output_nr++);
99 outputs[i].name = buf2;
100 outputs[i].handle = jack_port_register(client->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput , 0);
101 outputs[i].data = NULL;
102 if (!outputs[i].handle)
103 throw text_exception("Could not create JACK output port");
104 jack_port_set_alias(outputs[i].handle, (prefix + buf2).c_str());
108 void jack_host_base::close() {
109 port *inputs = get_inputs(), *outputs = get_outputs();
110 int input_count = get_input_count(), output_count = get_output_count();
111 for (int i = 0; i < input_count; i++) {
112 jack_port_unregister(client->client, inputs[i].handle);
113 inputs[i].data = NULL;
115 for (int i = 0; i < output_count; i++) {
116 jack_port_unregister(client->client, outputs[i].handle);
117 outputs[i].data = NULL;
119 if (get_midi())
120 jack_port_unregister(client->client, midi_port.handle);
121 client = NULL;
124 void destroy(GtkWindow *window, gpointer data)
126 gtk_main_quit();
129 void gui_win_destroy(GtkWindow *window, gpointer data)
133 static struct option long_options[] = {
134 {"help", 0, 0, 'h'},
135 {"version", 0, 0, 'v'},
136 {"client", 1, 0, 'c'},
137 {"effect", 0, 0, 'e'},
138 {"input", 1, 0, 'i'},
139 {"output", 1, 0, 'o'},
140 {"connect-midi", 1, 0, 'M'},
141 {0,0,0,0},
144 void print_help(char *argv[])
146 printf("JACK host for Calf effects\n"
147 "Syntax: %s [--client <name>] [--input <name>] [--output <name>] [--midi <name>]\n"
148 " [--connect-midi <name|capture-index>] [--help] [--version] [!] pluginname[:<preset>] [!] ...\n",
149 argv[0]);
152 int jack_client::do_jack_process(jack_nframes_t nframes, void *p)
154 jack_client *self = (jack_client *)p;
155 ptlock lock(self->mutex);
156 for(unsigned int i = 0; i < self->plugins.size(); i++)
157 self->plugins[i]->process(nframes);
158 return 0;
161 int jack_client::do_jack_bufsize(jack_nframes_t numsamples, void *p)
163 jack_client *self = (jack_client *)p;
164 ptlock lock(self->mutex);
165 for(unsigned int i = 0; i < self->plugins.size(); i++)
166 self->plugins[i]->cache_ports();
167 return 0;
170 void jack_client::delete_plugins()
172 ptlock lock(mutex);
173 for (unsigned int i = 0; i < plugins.size(); i++) {
174 // plugins[i]->close();
175 delete plugins[i];
177 plugins.clear();
180 struct host_session: public main_window_owner_iface, public calf_plugins::progress_report_iface
182 string client_name, input_name, output_name, midi_name;
183 vector<string> plugin_names;
184 map<int, string> presets;
185 #if USE_LASH
186 lash_client_t *lash_client;
187 lash_args_t *lash_args;
188 #endif
190 // these are not saved
191 jack_client client;
192 string autoconnect_midi;
193 set<int> chains;
194 vector<jack_host_base *> plugins;
195 main_window *main_win;
196 int lash_source_id;
197 bool restoring_session;
198 std::set<std::string> instances;
199 GtkWidget *progress_window;
201 host_session();
202 void open();
203 void add_plugin(string name, string preset, string instance_name = string());
204 void create_plugins_from_list();
205 void connect();
206 void close();
207 static gboolean update_lash(void *self) { ((host_session *)self)->update_lash(); return TRUE; }
208 void update_lash();
209 bool activate_preset(int plugin, const std::string &preset, bool builtin);
210 #if USE_LASH
211 void send_lash(LASH_Event_Type type, const std::string &data) {
212 lash_send_event(lash_client, lash_event_new_with_all(type, data.c_str()));
214 void send_config(const char *key, const std::string &data) {
215 char *buffer = new char[data.length()];
216 memcpy(buffer, data.data(), data.length());
217 lash_config_t *cfg = lash_config_new_with_key(strdup(key));
218 lash_config_set_value(cfg, buffer, data.length());
219 lash_send_config(lash_client, cfg);
221 #endif
222 virtual void new_plugin(const char *name);
223 virtual void remove_plugin(plugin_ctl_iface *plugin);
224 void remove_all_plugins();
225 std::string get_next_instance_name(const std::string &effect_name);
227 /// Create a toplevel window with progress bar
228 GtkWidget *create_progress_window();
229 /// Implementation of progress_report_iface function
230 void report_progress(float percentage, const std::string &message);
233 host_session::host_session()
235 client_name = "calf";
236 #if USE_LASH
237 lash_client = NULL;
238 lash_args = NULL;
239 #endif
240 lash_source_id = 0;
241 restoring_session = false;
242 main_win = new main_window;
243 main_win->set_owner(this);
244 progress_window = NULL;
247 std::string host_session::get_next_instance_name(const std::string &effect_name)
249 if (!instances.count(effect_name))
250 return effect_name;
251 for (int i = 2; ; i++)
253 string tmp = string(effect_name) + i2s(i);
254 if (!instances.count(tmp))
255 return tmp;
257 assert(0);
258 return "-";
261 void host_session::add_plugin(string name, string preset, string instance_name)
263 if (instance_name.empty())
264 instance_name = get_next_instance_name(name);
265 jack_host_base *jh = create_jack_host(name.c_str(), instance_name, this);
266 if (!jh) {
267 #ifdef ENABLE_EXPERIMENTAL
268 #else
269 #endif
270 string s =
271 #define PER_MODULE_ITEM(name, isSynth, jackname) jackname ", "
272 #include <calf/modulelist.h>
274 if (!s.empty())
275 s = s.substr(0, s.length() - 2);
276 throw text_exception("Unknown plugin name; allowed are: " + s);
278 instances.insert(jh->instance_name);
279 jh->open(&client);
281 plugins.push_back(jh);
282 client.add(jh);
283 main_win->add_plugin(jh);
284 if (!preset.empty()) {
285 if (!activate_preset(plugins.size() - 1, preset, false))
287 if (!activate_preset(plugins.size() - 1, preset, true))
289 fprintf(stderr, "Unknown preset: %s\n", preset.c_str());
295 void host_session::report_progress(float percentage, const std::string &message)
297 if (percentage < 100)
299 if (!progress_window) {
300 progress_window = create_progress_window();
301 gtk_window_set_modal (GTK_WINDOW (progress_window), TRUE);
302 if (main_win->toplevel)
303 gtk_window_set_transient_for (GTK_WINDOW (progress_window), main_win->toplevel);
305 gtk_widget_show(progress_window);
306 GtkWidget *pbar = gtk_bin_get_child (GTK_BIN (progress_window));
307 if (!message.empty())
308 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar), message.c_str());
309 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), percentage / 100.0);
311 else
313 if (progress_window) {
314 gtk_window_set_modal (GTK_WINDOW (progress_window), FALSE);
315 gtk_widget_destroy (progress_window);
316 progress_window = NULL;
320 while (gtk_events_pending ())
321 gtk_main_iteration ();
325 void host_session::create_plugins_from_list()
327 for (unsigned int i = 0; i < plugin_names.size(); i++) {
328 add_plugin(plugin_names[i], presets.count(i) ? presets[i] : string());
332 GtkWidget *host_session::create_progress_window()
334 GtkWidget *tlw = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
335 gtk_window_set_type_hint (GTK_WINDOW (tlw), GDK_WINDOW_TYPE_HINT_DIALOG);
336 GtkWidget *pbar = gtk_progress_bar_new();
337 gtk_container_add (GTK_CONTAINER(tlw), pbar);
338 gtk_widget_show_all (pbar);
339 return tlw;
342 void host_session::open()
344 if (!input_name.empty()) client.input_name = input_name;
345 if (!output_name.empty()) client.output_name = output_name;
346 if (!midi_name.empty()) client.midi_name = midi_name;
347 client.open(client_name.c_str());
348 main_win->prefix = client_name + " - ";
349 main_win->conditions.insert("jackhost");
350 main_win->conditions.insert("directlink");
351 if (!restoring_session)
352 create_plugins_from_list();
353 main_win->create();
354 gtk_signal_connect(GTK_OBJECT(main_win->toplevel), "destroy", G_CALLBACK(destroy), NULL);
357 void host_session::new_plugin(const char *name)
359 jack_host_base *jh = create_jack_host(name, get_next_instance_name(name), this);
360 if (!jh)
361 return;
362 instances.insert(jh->instance_name);
363 jh->open(&client);
365 plugins.push_back(jh);
366 client.add(jh);
367 main_win->add_plugin(jh);
370 void host_session::remove_plugin(plugin_ctl_iface *plugin)
372 for (unsigned int i = 0; i < plugins.size(); i++)
374 if (plugins[i] == plugin)
376 client.del(i);
377 plugins.erase(plugins.begin() + i);
378 main_win->del_plugin(plugin);
379 delete plugin;
380 return;
385 void host_session::remove_all_plugins()
387 while(!plugins.empty())
389 plugin_ctl_iface *plugin = plugins[0];
390 client.del(0);
391 plugins.erase(plugins.begin());
392 main_win->del_plugin(plugin);
393 delete plugin;
397 bool host_session::activate_preset(int plugin_no, const std::string &preset, bool builtin)
399 string cur_plugin = plugins[plugin_no]->get_id();
400 preset_vector &pvec = (builtin ? get_builtin_presets() : get_user_presets()).presets;
401 for (unsigned int i = 0; i < pvec.size(); i++) {
402 if (pvec[i].name == preset && pvec[i].plugin == cur_plugin)
404 pvec[i].activate(plugins[plugin_no]);
405 if (gui_win && gui_win->gui)
406 gui_win->gui->refresh();
407 return true;
410 return false;
413 void host_session::connect()
415 client.activate();
416 #if USE_LASH
417 if (lash_client)
418 lash_jack_client_name(lash_client, client.get_name().c_str());
419 #endif
420 if (!restoring_session)
422 string cnp = client.get_name() + ":";
423 for (unsigned int i = 0; i < plugins.size(); i++) {
424 if (chains.count(i)) {
425 if (!i)
427 if (plugins[0]->get_output_count() < 2)
429 fprintf(stderr, "Cannot connect input to plugin %s - incompatible ports\n", plugins[0]->name.c_str());
430 } else {
431 client.connect("system:capture_1", cnp + plugins[0]->get_inputs()[0].name);
432 client.connect("system:capture_2", cnp + plugins[0]->get_inputs()[1].name);
435 else
437 if (plugins[i - 1]->get_output_count() < 2 || plugins[i]->get_input_count() < 2)
439 fprintf(stderr, "Cannot connect plugins %s and %s - incompatible ports\n", plugins[i - 1]->name.c_str(), plugins[i]->name.c_str());
441 else {
442 client.connect(cnp + plugins[i - 1]->get_outputs()[0].name, cnp + plugins[i]->get_inputs()[0].name);
443 client.connect(cnp + plugins[i - 1]->get_outputs()[1].name, cnp + plugins[i]->get_inputs()[1].name);
448 if (chains.count(plugins.size()) && plugins.size())
450 int last = plugins.size() - 1;
451 if (plugins[last]->get_output_count() < 2)
453 fprintf(stderr, "Cannot connect plugin %s to output - incompatible ports\n", plugins[last]->name.c_str());
454 } else {
455 client.connect(cnp + plugins[last]->get_outputs()[0].name, "system:playback_1");
456 client.connect(cnp + plugins[last]->get_outputs()[1].name, "system:playback_2");
459 if (autoconnect_midi != "") {
460 for (unsigned int i = 0; i < plugins.size(); i++)
462 if (plugins[i]->get_midi())
463 client.connect(autoconnect_midi, cnp + plugins[i]->get_midi_port()->name);
467 #if USE_LASH
468 send_lash(LASH_Client_Name, "calf-"+client_name);
469 lash_source_id = g_timeout_add_full(G_PRIORITY_LOW, 250, update_lash, this, NULL); // 4 LASH reads per second... should be enough?
470 #endif
473 void host_session::close()
475 if (lash_source_id)
476 g_source_remove(lash_source_id);
477 main_win->on_closed();
478 main_win->close_guis();
479 client.deactivate();
480 client.delete_plugins();
481 client.close();
484 #if USE_LASH
486 static string stripfmt(string x)
488 if (x.length() < 2)
489 return x;
490 if (x.substr(x.length() - 2) != "%d")
491 return x;
492 return x.substr(0, x.length() - 2);
495 void host_session::update_lash()
497 do {
498 lash_event_t *event = lash_get_event(lash_client);
499 if (!event)
500 break;
502 // printf("type = %d\n", lash_event_get_type(event));
504 switch(lash_event_get_type(event)) {
505 case LASH_Save_Data_Set:
507 lash_config_t *cfg = lash_config_new_with_key("global");
508 dictionary tmp;
509 string pstr;
510 string i_name = stripfmt(client.input_name);
511 string o_name = stripfmt(client.output_name);
512 string m_name = stripfmt(client.midi_name);
513 tmp["input_prefix"] = i_name;
514 tmp["output_prefix"] = stripfmt(client.output_name);
515 tmp["midi_prefix"] = stripfmt(client.midi_name);
516 pstr = encode_map(tmp);
517 lash_config_set_value(cfg, pstr.c_str(), pstr.length());
518 lash_send_config(lash_client, cfg);
520 for (unsigned int i = 0; i < plugins.size(); i++) {
521 jack_host_base *p = plugins[i];
522 char ss[32];
523 plugin_preset preset;
524 preset.plugin = p->get_id();
525 preset.get_from(p);
526 sprintf(ss, "Plugin%d", i);
527 pstr = preset.to_xml();
528 tmp.clear();
529 tmp["instance_name"] = p->instance_name;
530 if (p->get_input_count())
531 tmp["input_name"] = p->get_inputs()[0].name.substr(i_name.length());
532 if (p->get_output_count())
533 tmp["output_name"] = p->get_outputs()[0].name.substr(o_name.length());
534 if (p->get_midi_port())
535 tmp["midi_name"] = p->get_midi_port()->name.substr(m_name.length());
536 tmp["preset"] = pstr;
537 pstr = encode_map(tmp);
538 lash_config_t *cfg = lash_config_new_with_key(ss);
539 lash_config_set_value(cfg, pstr.c_str(), pstr.length());
540 lash_send_config(lash_client, cfg);
542 send_lash(LASH_Save_Data_Set, "");
543 break;
546 case LASH_Restore_Data_Set:
548 // printf("!!!Restore data set!!!\n");
549 remove_all_plugins();
550 while(lash_config_t *cfg = lash_get_config(lash_client)) {
551 const char *key = lash_config_get_key(cfg);
552 // printf("key = %s\n", lash_config_get_key(cfg));
553 string data = string((const char *)lash_config_get_value(cfg), lash_config_get_value_size(cfg));
554 if (!strcmp(key, "global"))
556 dictionary dict;
557 decode_map(dict, data);
558 if (dict.count("input_prefix")) client.input_name = dict["input_prefix"]+"%d";
559 if (dict.count("output_prefix")) client.output_name = dict["output_prefix"]+"%d";
560 if (dict.count("midi_prefix")) client.midi_name = dict["midi_prefix"]+"%d";
562 if (!strncmp(key, "Plugin", 6))
564 unsigned int nplugin = atoi(key + 6);
565 dictionary dict;
566 decode_map(dict, data);
567 data = dict["preset"];
568 string instance_name;
569 if (dict.count("instance_name")) instance_name = dict["instance_name"];
570 if (dict.count("input_name")) client.input_nr = atoi(dict["input_name"].c_str());
571 if (dict.count("output_name")) client.output_nr = atoi(dict["output_name"].c_str());
572 if (dict.count("midi_name")) client.midi_nr = atoi(dict["midi_name"].c_str());
573 preset_list tmp;
574 tmp.parse("<presets>"+data+"</presets>");
575 if (tmp.presets.size())
577 printf("Load plugin %s\n", tmp.presets[0].plugin.c_str());
578 add_plugin(tmp.presets[0].plugin, "", instance_name);
579 tmp.presets[0].activate(plugins[nplugin]);
580 main_win->refresh_plugin(plugins[nplugin]);
583 lash_config_destroy(cfg);
585 send_lash(LASH_Restore_Data_Set, "");
586 break;
589 case LASH_Quit:
590 gtk_main_quit();
591 break;
593 default:
594 g_warning("Unhandled LASH event %d (%s)", lash_event_get_type(event), lash_event_get_string(event));
595 break;
597 } while(1);
599 #endif
601 host_session current_session;
603 int main(int argc, char *argv[])
605 host_session &sess = current_session;
606 gtk_init(&argc, &argv);
608 #if USE_LASH
609 for (int i = 1; i < argc; i++)
611 if (!strncmp(argv[i], "--lash-project=", 14)) {
612 sess.restoring_session = true;
613 break;
616 sess.lash_args = lash_extract_args(&argc, &argv);
617 sess.lash_client = lash_init(sess.lash_args, PACKAGE_NAME, LASH_Config_Data_Set, LASH_PROTOCOL(2, 0));
618 if (!sess.lash_client) {
619 g_warning("Warning: failed to create a LASH connection");
621 #endif
622 glade_init();
623 while(1) {
624 int option_index;
625 int c = getopt_long(argc, argv, "c:i:o:m:M:ehv", long_options, &option_index);
626 if (c == -1)
627 break;
628 switch(c) {
629 case 'h':
630 case '?':
631 print_help(argv);
632 return 0;
633 case 'v':
634 printf("%s\n", PACKAGE_STRING);
635 return 0;
636 case 'e':
637 fprintf(stderr, "Warning: switch -%c is deprecated!\n", c);
638 break;
639 case 'c':
640 sess.client_name = optarg;
641 break;
642 case 'i':
643 sess.input_name = string(optarg) + "_%d";
644 break;
645 case 'o':
646 sess.output_name = string(optarg) + "_%d";
647 break;
648 case 'm':
649 sess.midi_name = string(optarg) + "_%d";
650 break;
651 case 'M':
652 if (atoi(optarg))
653 sess.autoconnect_midi = "system:midi_capture_" + string(optarg);
654 else
655 sess.autoconnect_midi = string(optarg);
656 break;
659 while(optind < argc) {
660 if (!strcmp(argv[optind], "!")) {
661 sess.chains.insert(sess.plugin_names.size());
662 optind++;
663 } else {
664 string plugname = argv[optind++];
665 size_t pos = plugname.find(":");
666 if (pos != string::npos) {
667 sess.presets[sess.plugin_names.size()] = plugname.substr(pos + 1);
668 plugname = plugname.substr(0, pos);
670 sess.plugin_names.push_back(plugname);
673 try {
674 get_builtin_presets().load_defaults(true);
675 get_user_presets().load_defaults(false);
677 catch(calf_plugins::preset_exception &e)
679 // XXXKF this exception is already handled by load_defaults, so this is redundant
680 fprintf(stderr, "Error while loading presets: %s\n", e.what());
681 exit(1);
683 try {
684 sess.open();
685 sess.connect();
686 sess.client.activate();
687 gtk_main();
688 sess.close();
690 #if USE_LASH
691 if (sess.lash_args)
692 lash_args_destroy(sess.lash_args);
693 #endif
694 // this is now done on preset add
695 // save_presets(get_preset_filename().c_str());
697 catch(std::exception &e)
699 fprintf(stderr, "%s\n", e.what());
700 exit(1);
702 return 0;