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)
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
26 #include <glade/glade.h>
27 #include <jack/jack.h>
29 #include <lash/lash.h>
31 #include <calf/giface.h>
32 #include <calf/jackhost.h>
33 #include <calf/modules.h>
34 #include <calf/modules_dev.h>
36 #include <calf/preset.h>
37 #include <calf/preset_gui.h>
38 #include <calf/main_win.h>
39 #include <calf/utils.h>
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>
57 void jack_host_base::open(jack_client
*_client
)
59 client
= _client
; //jack_client_open(client_name, JackNullOption, &status);
69 void jack_host_base::create_ports() {
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());
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
;
120 jack_port_unregister(client
->client
, midi_port
.handle
);
124 void destroy(GtkWindow
*window
, gpointer data
)
129 void gui_win_destroy(GtkWindow
*window
, gpointer data
)
133 static struct option long_options
[] = {
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'},
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",
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
);
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();
170 void jack_client::delete_plugins()
173 for (unsigned int i
= 0; i
< plugins
.size(); i
++) {
174 // plugins[i]->close();
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
;
186 lash_client_t
*lash_client
;
187 lash_args_t
*lash_args
;
190 // these are not saved
192 string autoconnect_midi
;
194 vector
<jack_host_base
*> plugins
;
195 main_window
*main_win
;
197 bool restoring_session
;
198 std::set
<std::string
> instances
;
199 GtkWidget
*progress_window
;
203 void add_plugin(string name
, string preset
, string instance_name
= string());
204 void create_plugins_from_list();
207 static gboolean
update_lash(void *self
) { ((host_session
*)self
)->update_lash(); return TRUE
; }
209 bool activate_preset(int plugin
, const std::string
&preset
, bool builtin
);
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
);
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";
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
))
251 for (int i
= 2; ; i
++)
253 string tmp
= string(effect_name
) + i2s(i
);
254 if (!instances
.count(tmp
))
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);
268 #define PER_MODULE_ITEM(name, isSynth, jackname) jackname ", "
269 #include <calf/modulelist.h>
272 s
= s
.substr(0, s
.length() - 2);
273 throw text_exception("Unknown plugin name; allowed are: " + s
);
275 instances
.insert(jh
->instance_name
);
278 plugins
.push_back(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);
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
);
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();
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);
359 instances
.insert(jh
->instance_name
);
362 plugins
.push_back(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
)
374 plugins
.erase(plugins
.begin() + i
);
375 main_win
->del_plugin(plugin
);
382 void host_session::remove_all_plugins()
384 while(!plugins
.empty())
386 plugin_ctl_iface
*plugin
= plugins
[0];
388 plugins
.erase(plugins
.begin());
389 main_win
->del_plugin(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();
410 void host_session::connect()
415 lash_jack_client_name(lash_client
, client
.get_name().c_str());
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
)) {
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());
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
);
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());
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());
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
);
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?
470 void host_session::close()
473 g_source_remove(lash_source_id
);
474 main_win
->on_closed();
475 main_win
->close_guis();
477 client
.delete_plugins();
483 static string
stripfmt(string x
)
487 if (x
.substr(x
.length() - 2) != "%d")
489 return x
.substr(0, x
.length() - 2);
492 void host_session::update_lash()
495 lash_event_t
*event
= lash_get_event(lash_client
);
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");
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
];
520 plugin_preset preset
;
521 preset
.plugin
= p
->get_id();
523 sprintf(ss
, "Plugin%d", i
);
524 pstr
= preset
.to_xml();
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
, "");
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"))
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);
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());
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
, "");
591 g_warning("Unhandled LASH event %d (%s)", lash_event_get_type(event
), lash_event_get_string(event
));
598 host_session current_session
;
600 int main(int argc
, char *argv
[])
602 host_session
&sess
= current_session
;
603 gtk_init(&argc
, &argv
);
606 for (int i
= 1; i
< argc
; i
++)
608 if (!strncmp(argv
[i
], "--lash-project=", 14)) {
609 sess
.restoring_session
= true;
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");
622 int c
= getopt_long(argc
, argv
, "c:i:o:m:M:ehv", long_options
, &option_index
);
631 printf("%s\n", PACKAGE_STRING
);
634 fprintf(stderr
, "Warning: switch -%c is deprecated!\n", c
);
637 sess
.client_name
= optarg
;
640 sess
.input_name
= string(optarg
) + "_%d";
643 sess
.output_name
= string(optarg
) + "_%d";
646 sess
.midi_name
= string(optarg
) + "_%d";
650 sess
.autoconnect_midi
= "system:midi_capture_" + string(optarg
);
652 sess
.autoconnect_midi
= string(optarg
);
656 while(optind
< argc
) {
657 if (!strcmp(argv
[optind
], "!")) {
658 sess
.chains
.insert(sess
.plugin_names
.size());
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
);
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());
683 sess
.client
.activate();
689 lash_args_destroy(sess
.lash_args
);
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());