Line Graph: fix cairo radial gradient LED sim in portrait orientation
[calf.git] / src / jackhost.cpp
blob1869732cbb4ed93ee2e0ebdc5b6a36553e60b178
1 /* Calf DSP Library Utility Application - calfjackhost
2 * Standalone application module wrapper example.
4 * Copyright (C) 2007-2011 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 <jack/midiport.h>
22 #include <calf/host_session.h>
23 #include <calf/preset.h>
24 #include <calf/gtk_session_env.h>
25 #include <calf/plugin_tools.h>
26 #include <getopt.h>
28 using namespace std;
29 using namespace calf_utils;
30 using namespace calf_plugins;
32 const char *client_name = "calfhost";
34 extern "C" audio_module_iface *create_calf_plugin_by_name(const char *effect_name);
36 jack_host *calf_plugins::create_jack_host(jack_client *client, const char *effect_name, const std::string &instance_name, calf_plugins::progress_report_iface *priface)
38 audio_module_iface *plugin = create_calf_plugin_by_name(effect_name);
39 if (plugin != NULL)
40 return new jack_host(client, plugin, effect_name, instance_name, priface);
41 return NULL;
44 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
46 jack_host::jack_host(jack_client *_client, audio_module_iface *_module, const std::string &_name, const std::string &_instance_name, calf_plugins::progress_report_iface *_priface)
47 : module(_module)
49 name = _name;
50 instance_name = _instance_name;
52 client = _client;
53 cc_mappings = NULL;
54 changed = true;
56 module->get_port_arrays(ins, outs, params);
57 metadata = module->get_metadata_iface();
58 in_count = metadata->get_input_count();
59 out_count = metadata->get_output_count();
60 param_count = metadata->get_param_count();
61 inputs.resize(in_count);
62 outputs.resize(out_count);
63 param_values = new float[param_count];
64 write_serials.resize(param_count);
65 fill(write_serials.begin(), write_serials.end(), 0);
66 last_modify_serial = 0;
67 for (int i = 0; i < param_count; i++) {
68 params[i] = &param_values[i];
70 clear_preset();
71 midi_meter = 0;
72 last_designator = 0xFFFFFFFF;
73 module->set_progress_report_iface(_priface);
74 module->post_instantiate(client->sample_rate);
77 jack_host::~jack_host()
79 delete cc_mappings;
80 cc_mappings = NULL;
81 delete []param_values;
82 if (client)
83 destroy();
86 void jack_host::create()
88 create_ports();
89 cache_ports();
90 init_module();
92 changed = false;
95 void jack_host::create_ports() {
96 char buf[32];
97 char buf2[64];
98 string prefix = client->name + ":";
99 static const char *suffixes[] = { "#1", "#2", "#3", "#4", "#5", "#6", "#7", "#8" };
100 port *inputs = get_inputs();
101 port *outputs = get_outputs();
102 int in_count = metadata->get_input_count(), out_count = metadata->get_output_count();
103 for (int i=0; i<in_count; i++) {
104 snprintf(buf, sizeof(buf), "%s In %s", instance_name.c_str(), suffixes[i]);
105 snprintf(buf2, sizeof(buf2), client->input_name.c_str(), client->input_nr++);
106 inputs[i].nice_name = buf;
107 inputs[i].name = buf2;
108 inputs[i].handle = jack_port_register(client->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput , 0);
109 inputs[i].data = NULL;
110 inputs[i].meter.set_falloff(0.f, client->sample_rate);
111 if (!inputs[i].handle)
112 throw text_exception("Could not create JACK input port");
113 jack_port_set_alias(inputs[i].handle, (prefix + buf2).c_str());
115 if (metadata->get_midi()) {
116 snprintf(buf, sizeof(buf), "%s MIDI In", instance_name.c_str());
117 snprintf(buf2, sizeof(buf2), client->midi_name.c_str(), client->midi_nr++);
118 midi_port.nice_name = buf;
119 midi_port.name = buf2;
120 midi_port.handle = jack_port_register(client->client, buf, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
121 if (!midi_port.handle)
122 throw text_exception("Could not create JACK MIDI port");
123 jack_port_set_alias(midi_port.handle, (prefix + buf2).c_str());
125 for (int i=0; i<out_count; i++) {
126 snprintf(buf, sizeof(buf), "%s Out %s", instance_name.c_str(), suffixes[i]);
127 snprintf(buf2, sizeof(buf2), client->output_name.c_str(), client->output_nr++);
128 outputs[i].nice_name = buf;
129 outputs[i].name = buf2;
130 outputs[i].handle = jack_port_register(client->client, buf, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput , 0);
131 outputs[i].data = NULL;
132 if (!outputs[i].handle)
133 throw text_exception("Could not create JACK output port");
134 jack_port_set_alias(outputs[i].handle, (prefix + buf2).c_str());
138 void jack_host::handle_automation_cc(uint32_t designator, int value)
140 last_designator = designator;
141 if (!cc_mappings)
142 return;
143 automation_map::const_iterator i = cc_mappings->find(designator);
144 while (i != cc_mappings->end() && i->first == designator)
146 const automation_range &r = i->second;
147 const parameter_properties *props = metadata->get_param_props(r.param_no);
148 set_param_value(r.param_no, props->from_01(r.min_value + value * (r.max_value - r.min_value)/ 127.0));
149 write_serials[r.param_no] = ++last_modify_serial;
150 ++i;
154 uint32_t jack_host::get_last_automation_source()
156 return last_designator;
160 void jack_host::handle_event(uint8_t *buffer, uint32_t size)
162 int channel = buffer[0] & 15;
163 int value;
164 switch(buffer[0] >> 4)
166 case 8:
167 module->note_off(channel, buffer[1], buffer[2]);
168 break;
169 case 9:
170 if (!buffer[2])
171 module->note_off(channel, buffer[1], 0);
172 else
173 module->note_on(channel, buffer[1], buffer[2]);
174 break;
175 case 11:
176 module->control_change(channel, buffer[1], buffer[2]);
177 break;
178 case 12:
179 module->program_change(channel, buffer[1]);
180 break;
181 case 13:
182 module->channel_pressure(channel, buffer[1]);
183 break;
184 case 14:
185 value = buffer[1] + 128 * buffer[2] - 8192;
186 module->pitch_bend(channel, value);
187 break;
191 void jack_host::destroy()
193 port *inputs = get_inputs(), *outputs = get_outputs();
194 int input_count = metadata->get_input_count(), output_count = metadata->get_output_count();
195 for (int i = 0; i < input_count; i++) {
196 jack_port_unregister(client->client, inputs[i].handle);
197 inputs[i].data = NULL;
199 for (int i = 0; i < output_count; i++) {
200 jack_port_unregister(client->client, outputs[i].handle);
201 outputs[i].data = NULL;
203 if (metadata->get_midi())
204 jack_port_unregister(client->client, midi_port.handle);
205 client = NULL;
208 void jack_host::process_part(unsigned int time, unsigned int len)
210 if (!len)
211 return;
212 for (int i = 0; i < in_count; i++)
213 inputs[i].meter.update(ins[i] + time, len);
214 unsigned int mask = module->process_slice(time, time + len);
215 for (int i = 0; i < out_count; i++)
217 if (!(mask & (1 << i))) {
218 dsp::zero(outs[i] + time, len);
219 outputs[i].meter.update_zeros(len);
220 } else
221 outputs[i].meter.update(outs[i] + time, len);
223 // decay linearly for 0.1s
224 float new_meter = midi_meter - len / (0.1 * client->sample_rate);
225 if (new_meter < 0)
226 new_meter = 0;
227 midi_meter = new_meter;
230 float jack_host::get_level(unsigned int port)
232 if (port < (unsigned)in_count)
233 return inputs[port].meter.level;
234 port -= in_count;
235 if (port < (unsigned)out_count)
236 return outputs[port].meter.level;
237 port -= out_count;
238 if (port == 0 && metadata->get_midi())
239 return midi_meter;
240 return 0.f;
243 int jack_host::process(jack_nframes_t nframes, automation_iface &automation)
245 for (int i=0; i<in_count; i++) {
246 ins[i] = inputs[i].data = (float *)jack_port_get_buffer(inputs[i].handle, nframes);
248 if (metadata->get_midi())
249 midi_port.data = (float *)jack_port_get_buffer(midi_port.handle, nframes);
250 if (changed) {
251 module->params_changed();
252 changed = false;
255 unsigned int time = 0;
256 if (metadata->get_midi())
258 jack_midi_event_t event;
259 int count = jack_midi_get_event_count(midi_port.data NFRAMES_MAYBE(nframes));
260 for (int i = 0; i < count; i++)
262 jack_midi_event_get(&event, midi_port.data, i NFRAMES_MAYBE(nframes));
263 uint32_t endtime = automation.apply_and_adjust(time, event.time);
264 unsigned int len = endtime - time;
265 process_part(time, len);
267 midi_meter = 1.f;
268 handle_event(event.buffer, event.size);
270 time = event.time;
273 while(time < nframes)
275 uint32_t endtime = automation.apply_and_adjust(time, nframes);
276 process_part(time, endtime - time);
277 time = endtime;
279 module->params_reset();
280 return 0;
283 void jack_host::init_module()
285 module->set_sample_rate(client->sample_rate);
286 module->activate();
287 module->params_changed();
290 void jack_host::cache_ports()
292 for (int i=0; i<out_count; i++) {
293 outs[i] = outputs[i].data = (float *)jack_port_get_buffer(outputs[i].handle, 0);
297 void jack_host::get_all_input_ports(std::vector<port *> &ports)
299 for (int i = 0; i < in_count; i++)
300 ports.push_back(&inputs[i]);
301 if (metadata->get_midi())
302 ports.push_back(&midi_port);
305 void jack_host::get_all_output_ports(std::vector<port *> &ports)
307 for (int i = 0; i < out_count; i++)
308 ports.push_back(&outputs[i]);
311 static void remove_mapping(automation_map &amap, uint32_t source, int param_no)
313 for(automation_map::iterator i = amap.find(source); i != amap.end() && i->first == source; )
315 automation_map::iterator j = i;
316 ++j;
317 if (i->second.param_no == param_no)
318 amap.erase(i);
319 i = j;
323 void jack_host::add_automation(uint32_t source, const automation_range &dest)
325 automation_map *amap = new automation_map;
326 if (cc_mappings)
327 amap->insert(cc_mappings->begin(), cc_mappings->end());
328 remove_mapping(*amap, source, dest.param_no);
329 amap->insert(make_pair(source, dest));
330 replace_automation_map(amap);
333 void jack_host::delete_automation(uint32_t source, int param_no)
335 automation_map *amap = new automation_map;
336 if (cc_mappings)
337 amap->insert(cc_mappings->begin(), cc_mappings->end());
338 remove_mapping(*amap, source, param_no);
339 replace_automation_map(amap);
342 void jack_host::replace_automation_map(automation_map *amap)
344 client->atomic_swap(cc_mappings, amap);
345 delete amap;
348 void jack_host::get_automation(int param_no, multimap<uint32_t, automation_range> &dests)
350 dests.clear();
351 if (!cc_mappings)
352 return;
353 for(automation_map::iterator i = cc_mappings->begin(); i != cc_mappings->end(); ++i)
355 if (param_no == -1 || param_no == i->second.param_no)
356 dests.insert(*i);
360 void jack_host::send_automation_configures(send_configure_iface *sci)
362 if (!cc_mappings)
363 return;
364 for(automation_map::iterator i = cc_mappings->begin(); i != cc_mappings->end(); ++i)
366 i->second.send_configure(metadata, i->first, sci);
370 char *jack_host::configure(const char *key, const char *value)
372 uint32_t controller;
373 automation_range *ar = automation_range::new_from_configure(metadata, key, value, controller);
374 if (ar)
376 add_automation(controller, *ar);
377 delete ar;
378 return NULL;
380 return module->configure(key, value);
383 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
385 static const char *short_options = "c:i:l:o:m:M:s:S:ehv";
387 static struct option long_options[] = {
388 {"help", 0, 0, 'h'},
389 {"version", 0, 0, 'v'},
390 {"client", 1, 0, 'c'},
391 {"effect", 0, 0, 'e'},
392 {"load", 1, 0, 'l'},
393 {"input", 1, 0, 'i'},
394 {"output", 1, 0, 'o'},
395 {"state", 1, 0, 's'},
396 {"connect-midi", 1, 0, 'M'},
397 {"session-id", 1, 0, 'S'},
398 {0,0,0,0},
401 void print_help(char *argv[])
403 printf("JACK host for Calf effects\n"
404 "Syntax: %s [--client <name>] [--input <name>] [--output <name>] [--midi <name>] [--load|state <session>]\n"
405 " [--connect-midi <name|capture-index>] [--help] [--version] [!] pluginname[:<preset>] [!] ...\n",
406 argv[0]);
409 int main(int argc, char *argv[])
411 #if !GLIB_CHECK_VERSION(2, 36, 0)
412 g_type_init();
413 #endif
414 #if !GLIB_CHECK_VERSION(2, 32, 0)
415 if (!g_thread_supported()) g_thread_init(NULL);
416 #endif
418 host_session sess(new gtk_session_environment());
419 if (argc > 0 && argv[0] && argv[0] != sess.calfjackhost_cmd)
421 char *path = realpath(argv[0], NULL);
422 sess.calfjackhost_cmd = path;
423 free(path);
425 sess.session_env->init_gui(argc, argv);
427 // Scan the options for the first time to find switches like --help, -h or -?
428 // This avoids starting communication with LASH when displaying help text.
429 while(1)
431 int option_index;
432 int c = getopt_long(argc, argv, short_options, long_options, &option_index);
433 if (c == -1)
434 break;
435 if (c == 'h' || c == '?')
437 print_help(argv);
438 return 0;
441 // Rewind options to start
442 optind = 1;
444 #if USE_LASH
445 sess.session_manager = create_lash_session_mgr(&sess, argc, argv);
446 #else
447 sess.session_manager = NULL;
448 #endif
449 while(1)
451 int option_index;
452 int c = getopt_long(argc, argv, short_options, long_options, &option_index);
453 if (c == -1)
454 break;
455 switch(c) {
456 case 'v':
457 printf("%s\n", PACKAGE_STRING);
458 return 0;
459 case 'e':
460 fprintf(stderr, "Warning: switch -%c is deprecated!\n", c);
461 break;
462 case 'c':
463 sess.client_name = optarg;
464 break;
465 case 'i':
466 sess.input_name = string(optarg) + "_%d";
467 break;
468 case 'o':
469 sess.output_name = string(optarg) + "_%d";
470 break;
471 case 'm':
472 sess.midi_name = string(optarg) + "_%d";
473 break;
474 case 'S':
475 sess.jack_session_id = optarg;
476 break;
477 case 'l':
478 case 's':
480 if (!g_path_is_absolute(optarg))
482 gchar *curdir = g_get_current_dir();
483 gchar *str = g_build_filename(curdir, optarg, NULL);
484 sess.load_name = str;
485 g_free(str);
486 g_free(curdir);
488 else
489 sess.load_name = optarg;
490 sess.only_load_if_exists = (c == 's');
491 break;
493 case 'M':
494 if (atoi(optarg)) {
495 sess.autoconnect_midi_index = atoi(optarg);
497 else
498 sess.autoconnect_midi = string(optarg);
499 break;
502 while(optind < argc) {
503 if (!strcmp(argv[optind], "!")) {
504 sess.chains.insert(sess.plugin_names.size());
505 optind++;
506 } else {
507 string plugname = argv[optind++];
508 size_t pos = plugname.find(":");
509 if (pos != string::npos) {
510 sess.presets[sess.plugin_names.size()] = plugname.substr(pos + 1);
511 plugname = plugname.substr(0, pos);
513 sess.plugin_names.push_back(plugname);
516 try {
517 get_builtin_presets().load_defaults(true);
518 get_user_presets().load_defaults(false);
520 catch(calf_plugins::preset_exception &e)
522 // XXXKF this exception is already handled by load_defaults, so this is redundant
523 fprintf(stderr, "Error while loading presets: %s\n", e.what());
524 exit(1);
526 try {
527 sess.open();
528 sess.connect();
529 sess.client.activate();
530 sess.set_signal_handlers();
531 sess.session_env->start_gui_loop();
532 sess.close();
534 catch(std::exception &e)
536 fprintf(stderr, "%s\n", e.what());
537 exit(1);
539 return 0;