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)
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
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>
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
);
40 return new jack_host(client
, plugin
, effect_name
, instance_name
, priface
);
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
)
50 instance_name
= _instance_name
;
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
] = ¶m_values
[i
];
72 last_designator
= 0xFFFFFFFF;
73 module
->set_progress_report_iface(_priface
);
74 module
->post_instantiate(client
->sample_rate
);
77 jack_host::~jack_host()
81 delete []param_values
;
86 void jack_host::create()
95 void jack_host::create_ports() {
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
;
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
;
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;
164 switch(buffer
[0] >> 4)
167 module
->note_off(channel
, buffer
[1], buffer
[2]);
171 module
->note_off(channel
, buffer
[1], 0);
173 module
->note_on(channel
, buffer
[1], buffer
[2]);
176 module
->control_change(channel
, buffer
[1], buffer
[2]);
179 module
->program_change(channel
, buffer
[1]);
182 module
->channel_pressure(channel
, buffer
[1]);
185 value
= buffer
[1] + 128 * buffer
[2] - 8192;
186 module
->pitch_bend(channel
, value
);
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
);
208 void jack_host::process_part(unsigned int time
, unsigned int len
)
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
);
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
);
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
;
235 if (port
< (unsigned)out_count
)
236 return outputs
[port
].meter
.level
;
238 if (port
== 0 && metadata
->get_midi())
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
);
251 module
->params_changed();
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
);
268 handle_event(event
.buffer
, event
.size
);
273 while(time
< nframes
)
275 uint32_t endtime
= automation
.apply_and_adjust(time
, nframes
);
276 process_part(time
, endtime
- time
);
279 module
->params_reset();
283 void jack_host::init_module()
285 module
->set_sample_rate(client
->sample_rate
);
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
;
317 if (i
->second
.param_no
== param_no
)
323 void jack_host::add_automation(uint32_t source
, const automation_range
&dest
)
325 automation_map
*amap
= new automation_map
;
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
;
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
);
348 void jack_host::get_automation(int param_no
, multimap
<uint32_t, automation_range
> &dests
)
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
)
360 void jack_host::send_automation_configures(send_configure_iface
*sci
)
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
)
373 automation_range
*ar
= automation_range::new_from_configure(metadata
, key
, value
, controller
);
376 add_automation(controller
, *ar
);
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
[] = {
389 {"version", 0, 0, 'v'},
390 {"client", 1, 0, 'c'},
391 {"effect", 0, 0, 'e'},
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'},
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",
409 int main(int argc
, char *argv
[])
411 #if !GLIB_CHECK_VERSION(2, 36, 0)
414 #if !GLIB_CHECK_VERSION(2, 32, 0)
415 if (!g_thread_supported()) g_thread_init(NULL
);
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
;
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.
432 int c
= getopt_long(argc
, argv
, short_options
, long_options
, &option_index
);
435 if (c
== 'h' || c
== '?')
441 // Rewind options to start
445 sess
.session_manager
= create_lash_session_mgr(&sess
, argc
, argv
);
447 sess
.session_manager
= NULL
;
452 int c
= getopt_long(argc
, argv
, short_options
, long_options
, &option_index
);
457 printf("%s\n", PACKAGE_STRING
);
460 fprintf(stderr
, "Warning: switch -%c is deprecated!\n", c
);
463 sess
.client_name
= optarg
;
466 sess
.input_name
= string(optarg
) + "_%d";
469 sess
.output_name
= string(optarg
) + "_%d";
472 sess
.midi_name
= string(optarg
) + "_%d";
475 sess
.jack_session_id
= optarg
;
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
;
489 sess
.load_name
= optarg
;
490 sess
.only_load_if_exists
= (c
== 's');
495 sess
.autoconnect_midi_index
= atoi(optarg
);
498 sess
.autoconnect_midi
= string(optarg
);
502 while(optind
< argc
) {
503 if (!strcmp(argv
[optind
], "!")) {
504 sess
.chains
.insert(sess
.plugin_names
.size());
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
);
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());
529 sess
.client
.activate();
530 sess
.set_signal_handlers();
531 sess
.session_env
->start_gui_loop();
534 catch(std::exception
&e
)
536 fprintf(stderr
, "%s\n", e
.what());