+ Monosynth: precalculate tables in post_instantiate not activate
[calf.git] / src / jackhost.cpp
blobc9603db87cbefc1478dad4d939e96fd0375c83cf
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 string s =
268 #define PER_MODULE_ITEM(name, isSynth, jackname) jackname ", "
269 #include <calf/modulelist.h>
271 if (!s.empty())
272 s = s.substr(0, s.length() - 2);
273 throw text_exception("Unknown plugin name; allowed are: " + s);
275 instances.insert(jh->instance_name);
276 jh->open(&client);
278 plugins.push_back(jh);
279 client.add(jh);
280 main_win->add_plugin(jh);
281 if (!preset.empty()) {
282 if (!activate_preset(plugins.size() - 1, preset, false))
284 if (!activate_preset(plugins.size() - 1, preset, true))
286 fprintf(stderr, "Unknown preset: %s\n", preset.c_str());
292 void host_session::report_progress(float percentage, const std::string &message)
294 if (percentage < 100)
296 if (!progress_window) {
297 progress_window = create_progress_window();
298 gtk_window_set_modal (GTK_WINDOW (progress_window), TRUE);
299 if (main_win->toplevel)
300 gtk_window_set_transient_for (GTK_WINDOW (progress_window), main_win->toplevel);
302 gtk_widget_show(progress_window);
303 GtkWidget *pbar = gtk_bin_get_child (GTK_BIN (progress_window));
304 if (!message.empty())
305 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (pbar), message.c_str());
306 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (pbar), percentage / 100.0);
308 else
310 if (progress_window) {
311 gtk_window_set_modal (GTK_WINDOW (progress_window), FALSE);
312 gtk_widget_destroy (progress_window);
313 progress_window = NULL;
317 while (gtk_events_pending ())
318 gtk_main_iteration ();
322 void host_session::create_plugins_from_list()
324 for (unsigned int i = 0; i < plugin_names.size(); i++) {
325 add_plugin(plugin_names[i], presets.count(i) ? presets[i] : string());
329 GtkWidget *host_session::create_progress_window()
331 GtkWidget *tlw = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
332 gtk_window_set_type_hint (GTK_WINDOW (tlw), GDK_WINDOW_TYPE_HINT_DIALOG);
333 GtkWidget *pbar = gtk_progress_bar_new();
334 gtk_container_add (GTK_CONTAINER(tlw), pbar);
335 gtk_widget_show_all (pbar);
336 return tlw;
339 void host_session::open()
341 if (!input_name.empty()) client.input_name = input_name;
342 if (!output_name.empty()) client.output_name = output_name;
343 if (!midi_name.empty()) client.midi_name = midi_name;
344 client.open(client_name.c_str());
345 main_win->prefix = client_name + " - ";
346 main_win->conditions.insert("jackhost");
347 main_win->conditions.insert("directlink");
348 if (!restoring_session)
349 create_plugins_from_list();
350 main_win->create();
351 gtk_signal_connect(GTK_OBJECT(main_win->toplevel), "destroy", G_CALLBACK(destroy), NULL);
354 void host_session::new_plugin(const char *name)
356 jack_host_base *jh = create_jack_host(name, get_next_instance_name(name), this);
357 if (!jh)
358 return;
359 instances.insert(jh->instance_name);
360 jh->open(&client);
362 plugins.push_back(jh);
363 client.add(jh);
364 main_win->add_plugin(jh);
367 void host_session::remove_plugin(plugin_ctl_iface *plugin)
369 for (unsigned int i = 0; i < plugins.size(); i++)
371 if (plugins[i] == plugin)
373 client.del(i);
374 plugins.erase(plugins.begin() + i);
375 main_win->del_plugin(plugin);
376 delete plugin;
377 return;
382 void host_session::remove_all_plugins()
384 while(!plugins.empty())
386 plugin_ctl_iface *plugin = plugins[0];
387 client.del(0);
388 plugins.erase(plugins.begin());
389 main_win->del_plugin(plugin);
390 delete plugin;
394 bool host_session::activate_preset(int plugin_no, const std::string &preset, bool builtin)
396 string cur_plugin = plugins[plugin_no]->get_id();
397 preset_vector &pvec = (builtin ? get_builtin_presets() : get_user_presets()).presets;
398 for (unsigned int i = 0; i < pvec.size(); i++) {
399 if (pvec[i].name == preset && pvec[i].plugin == cur_plugin)
401 pvec[i].activate(plugins[plugin_no]);
402 if (gui_win && gui_win->gui)
403 gui_win->gui->refresh();
404 return true;
407 return false;
410 void host_session::connect()
412 client.activate();
413 #if USE_LASH
414 if (lash_client)
415 lash_jack_client_name(lash_client, client.get_name().c_str());
416 #endif
417 if (!restoring_session)
419 string cnp = client.get_name() + ":";
420 for (unsigned int i = 0; i < plugins.size(); i++) {
421 if (chains.count(i)) {
422 if (!i)
424 if (plugins[0]->get_output_count() < 2)
426 fprintf(stderr, "Cannot connect input to plugin %s - incompatible ports\n", plugins[0]->name.c_str());
427 } else {
428 client.connect("system:capture_1", cnp + plugins[0]->get_inputs()[0].name);
429 client.connect("system:capture_2", cnp + plugins[0]->get_inputs()[1].name);
432 else
434 if (plugins[i - 1]->get_output_count() < 2 || plugins[i]->get_input_count() < 2)
436 fprintf(stderr, "Cannot connect plugins %s and %s - incompatible ports\n", plugins[i - 1]->name.c_str(), plugins[i]->name.c_str());
438 else {
439 client.connect(cnp + plugins[i - 1]->get_outputs()[0].name, cnp + plugins[i]->get_inputs()[0].name);
440 client.connect(cnp + plugins[i - 1]->get_outputs()[1].name, cnp + plugins[i]->get_inputs()[1].name);
445 if (chains.count(plugins.size()) && plugins.size())
447 int last = plugins.size() - 1;
448 if (plugins[last]->get_output_count() < 2)
450 fprintf(stderr, "Cannot connect plugin %s to output - incompatible ports\n", plugins[last]->name.c_str());
451 } else {
452 client.connect(cnp + plugins[last]->get_outputs()[0].name, "system:playback_1");
453 client.connect(cnp + plugins[last]->get_outputs()[1].name, "system:playback_2");
456 if (autoconnect_midi != "") {
457 for (unsigned int i = 0; i < plugins.size(); i++)
459 if (plugins[i]->get_midi())
460 client.connect(autoconnect_midi, cnp + plugins[i]->get_midi_port()->name);
464 #if USE_LASH
465 send_lash(LASH_Client_Name, "calf-"+client_name);
466 lash_source_id = g_timeout_add_full(G_PRIORITY_LOW, 250, update_lash, this, NULL); // 4 LASH reads per second... should be enough?
467 #endif
470 void host_session::close()
472 if (lash_source_id)
473 g_source_remove(lash_source_id);
474 main_win->on_closed();
475 main_win->close_guis();
476 client.deactivate();
477 client.delete_plugins();
478 client.close();
481 #if USE_LASH
483 static string stripfmt(string x)
485 if (x.length() < 2)
486 return x;
487 if (x.substr(x.length() - 2) != "%d")
488 return x;
489 return x.substr(0, x.length() - 2);
492 void host_session::update_lash()
494 do {
495 lash_event_t *event = lash_get_event(lash_client);
496 if (!event)
497 break;
499 // printf("type = %d\n", lash_event_get_type(event));
501 switch(lash_event_get_type(event)) {
502 case LASH_Save_Data_Set:
504 lash_config_t *cfg = lash_config_new_with_key("global");
505 dictionary tmp;
506 string pstr;
507 string i_name = stripfmt(client.input_name);
508 string o_name = stripfmt(client.output_name);
509 string m_name = stripfmt(client.midi_name);
510 tmp["input_prefix"] = i_name;
511 tmp["output_prefix"] = stripfmt(client.output_name);
512 tmp["midi_prefix"] = stripfmt(client.midi_name);
513 pstr = encode_map(tmp);
514 lash_config_set_value(cfg, pstr.c_str(), pstr.length());
515 lash_send_config(lash_client, cfg);
517 for (unsigned int i = 0; i < plugins.size(); i++) {
518 jack_host_base *p = plugins[i];
519 char ss[32];
520 plugin_preset preset;
521 preset.plugin = p->get_id();
522 preset.get_from(p);
523 sprintf(ss, "Plugin%d", i);
524 pstr = preset.to_xml();
525 tmp.clear();
526 tmp["instance_name"] = p->instance_name;
527 if (p->get_input_count())
528 tmp["input_name"] = p->get_inputs()[0].name.substr(i_name.length());
529 if (p->get_output_count())
530 tmp["output_name"] = p->get_outputs()[0].name.substr(o_name.length());
531 if (p->get_midi_port())
532 tmp["midi_name"] = p->get_midi_port()->name.substr(m_name.length());
533 tmp["preset"] = pstr;
534 pstr = encode_map(tmp);
535 lash_config_t *cfg = lash_config_new_with_key(ss);
536 lash_config_set_value(cfg, pstr.c_str(), pstr.length());
537 lash_send_config(lash_client, cfg);
539 send_lash(LASH_Save_Data_Set, "");
540 break;
543 case LASH_Restore_Data_Set:
545 // printf("!!!Restore data set!!!\n");
546 remove_all_plugins();
547 while(lash_config_t *cfg = lash_get_config(lash_client)) {
548 const char *key = lash_config_get_key(cfg);
549 // printf("key = %s\n", lash_config_get_key(cfg));
550 string data = string((const char *)lash_config_get_value(cfg), lash_config_get_value_size(cfg));
551 if (!strcmp(key, "global"))
553 dictionary dict;
554 decode_map(dict, data);
555 if (dict.count("input_prefix")) client.input_name = dict["input_prefix"]+"%d";
556 if (dict.count("output_prefix")) client.output_name = dict["output_prefix"]+"%d";
557 if (dict.count("midi_prefix")) client.midi_name = dict["midi_prefix"]+"%d";
559 if (!strncmp(key, "Plugin", 6))
561 unsigned int nplugin = atoi(key + 6);
562 dictionary dict;
563 decode_map(dict, data);
564 data = dict["preset"];
565 string instance_name;
566 if (dict.count("instance_name")) instance_name = dict["instance_name"];
567 if (dict.count("input_name")) client.input_nr = atoi(dict["input_name"].c_str());
568 if (dict.count("output_name")) client.output_nr = atoi(dict["output_name"].c_str());
569 if (dict.count("midi_name")) client.midi_nr = atoi(dict["midi_name"].c_str());
570 preset_list tmp;
571 tmp.parse("<presets>"+data+"</presets>");
572 if (tmp.presets.size())
574 printf("Load plugin %s\n", tmp.presets[0].plugin.c_str());
575 add_plugin(tmp.presets[0].plugin, "", instance_name);
576 tmp.presets[0].activate(plugins[nplugin]);
577 main_win->refresh_plugin(plugins[nplugin]);
580 lash_config_destroy(cfg);
582 send_lash(LASH_Restore_Data_Set, "");
583 break;
586 case LASH_Quit:
587 gtk_main_quit();
588 break;
590 default:
591 g_warning("Unhandled LASH event %d (%s)", lash_event_get_type(event), lash_event_get_string(event));
592 break;
594 } while(1);
596 #endif
598 host_session current_session;
600 int main(int argc, char *argv[])
602 host_session &sess = current_session;
603 gtk_init(&argc, &argv);
605 #if USE_LASH
606 for (int i = 1; i < argc; i++)
608 if (!strncmp(argv[i], "--lash-project=", 14)) {
609 sess.restoring_session = true;
610 break;
613 sess.lash_args = lash_extract_args(&argc, &argv);
614 sess.lash_client = lash_init(sess.lash_args, PACKAGE_NAME, LASH_Config_Data_Set, LASH_PROTOCOL(2, 0));
615 if (!sess.lash_client) {
616 g_warning("Warning: failed to create a LASH connection");
618 #endif
619 glade_init();
620 while(1) {
621 int option_index;
622 int c = getopt_long(argc, argv, "c:i:o:m:M:ehv", long_options, &option_index);
623 if (c == -1)
624 break;
625 switch(c) {
626 case 'h':
627 case '?':
628 print_help(argv);
629 return 0;
630 case 'v':
631 printf("%s\n", PACKAGE_STRING);
632 return 0;
633 case 'e':
634 fprintf(stderr, "Warning: switch -%c is deprecated!\n", c);
635 break;
636 case 'c':
637 sess.client_name = optarg;
638 break;
639 case 'i':
640 sess.input_name = string(optarg) + "_%d";
641 break;
642 case 'o':
643 sess.output_name = string(optarg) + "_%d";
644 break;
645 case 'm':
646 sess.midi_name = string(optarg) + "_%d";
647 break;
648 case 'M':
649 if (atoi(optarg))
650 sess.autoconnect_midi = "system:midi_capture_" + string(optarg);
651 else
652 sess.autoconnect_midi = string(optarg);
653 break;
656 while(optind < argc) {
657 if (!strcmp(argv[optind], "!")) {
658 sess.chains.insert(sess.plugin_names.size());
659 optind++;
660 } else {
661 string plugname = argv[optind++];
662 size_t pos = plugname.find(":");
663 if (pos != string::npos) {
664 sess.presets[sess.plugin_names.size()] = plugname.substr(pos + 1);
665 plugname = plugname.substr(0, pos);
667 sess.plugin_names.push_back(plugname);
670 try {
671 get_builtin_presets().load_defaults(true);
672 get_user_presets().load_defaults(false);
674 catch(calf_plugins::preset_exception &e)
676 // XXXKF this exception is already handled by load_defaults, so this is redundant
677 fprintf(stderr, "Error while loading presets: %s\n", e.what());
678 exit(1);
680 try {
681 sess.open();
682 sess.connect();
683 sess.client.activate();
684 gtk_main();
685 sess.close();
687 #if USE_LASH
688 if (sess.lash_args)
689 lash_args_destroy(sess.lash_args);
690 #endif
691 // this is now done on preset add
692 // save_presets(get_preset_filename().c_str());
694 catch(std::exception &e)
696 fprintf(stderr, "%s\n", e.what());
697 exit(1);
699 return 0;