1 /*
2 * Copyright (C) 2009, 2010 Hermann Meyer, James Warden, Andreas Degert
3 * Copyright (C) 2011 Pete Shorthose
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * ---------------------------------------------------------------------------
19 *
20 * This is the gx_head system interface
21 *
22 * ----------------------------------------------------------------------------
23 */
24
25 #include <dirent.h>
26 #include <iostream>
27 #include <iomanip> // NOLINT
28
29 #include "engine.h"
30
31 namespace gx_system {
32
33 /****************************************************************
34 ** Measuring times (only when debugging)
35 */
36
37 #ifndef NDEBUG
38
reset()39 void Measure::reset() {
40 period.reset();
41 duration.reset();
42 duration1.reset();
43 duration2.reset();
44 FPUStatus1 = 0;
45 FPUStatus2 = 0;
46 MXStatus1 = 0;
47 MXStatus2 = 0;
48 }
49
print_accum(const Accum & accum,const char * prefix,bool verbose,int total) const50 void Measure::print_accum(const Accum& accum, const char* prefix, bool verbose, int total) const {
51 streamsize prec = cout.precision();
52 ios_base::fmtflags flags = cout.flags();
53 cout << prefix << "mean: " << fixed << setprecision(4) << ns2ms(accum.mean());
54 if (total > 0) {
55 cout << " (" << setprecision(2) << 100.0*accum.mean()/static_cast<float>(total) << "%)";
56 }
57 cout << ", min: " << setprecision(4) << ns2ms(accum.minimum())
58 << ", max: " << ns2ms(accum.maximum());
59 if (total > 0) {
60 cout << " (" << setprecision(2) << 100.0*accum.maximum()/static_cast<float>(total) << "%)";
61 }
62 if (verbose) {
63 cout << ", stddev: " << setprecision(4) << ns2ms(accum.stddev())
64 << ", n: " << accum.count();
65 }
66 cout << endl;
67 cout.precision(prec);
68 cout.flags(flags);
69 }
70
print_status(const char * title,unsigned int status)71 static void print_status(const char *title, unsigned int status) {
72 // print list of names for active bits in "status"
73 // bits for mmx and x87 have different symbolic names in
74 // header files but are actually identical so that this function
75 // can be used for both types status word
76 Glib::ustring s;
77 if (status & FE_INVALID) {
78 if (!s.empty()) {
79 s += ",";
80 }
81 s += "invalid";
82 }
83 #ifdef FE_DENORM
84 if (status & FE_DENORM) {
85 if (!s.empty()) {
86 s += ",";
87 }
88 s += "denormal";
89 }
90 #endif
91 if (status & FE_DIVBYZERO) {
92 if (!s.empty()) {
93 s += ",";
94 }
95 s += "zerodivide";
96 }
97 if (status & FE_OVERFLOW) {
98 if (!s.empty()) {
99 s += ",";
100 }
101 s += "overflow";
102 }
103 if (status & FE_UNDERFLOW) {
104 if (!s.empty()) {
105 s += ",";
106 }
107 s += "underflow";
108 }
109 if (!s.empty()) {
110 cout << title << s << endl;
111 }
112 }
113
print(bool verbose) const114 void Measure::print(bool verbose) const {
115 if (verbose) {
116 print_accum(period, "period ", verbose);
117 print_accum(duration1, "duration1 ", verbose, period.mean());
118 print_accum(duration2, "duration2 ", verbose, period.mean());
119 print_accum(duration, "duration ", verbose, period.mean());
120 } else {
121 print_accum(duration, "duration ", false, period.mean());
122 }
123 print_status("FPU status: ", FPUStatus1 | FPUStatus2);
124 print_status("MX status: ", MXStatus1 | MXStatus2);
125 }
126
MeasureThreadsafe()127 MeasureThreadsafe::MeasureThreadsafe()
128 : m(), pmeasure(m), t1s(), t1e(), t2s(), t1old(), FPUStatus(), MXStatus() {
129 }
130
print(bool verbose)131 void MeasureThreadsafe::print(bool verbose) {
132 Measure *p = pmeasure;
133 Measure *pn;
134 if (p == m) {
135 pn = m+1;
136 } else {
137 pn = m;
138 }
139 atomic_set(&pmeasure, pn);
140 p->print(verbose);
141 p->reset();
142 }
143
144 MeasureThreadsafe measure;
145
add_time_measurement()146 void add_time_measurement() {
147 char *p = getenv("GUITARIX_MEASURE");
148 if (!p) {
149 return;
150 }
151 bool verbose = false;
152 if (strcmp(p, "1") == 0) {
153 verbose = true;
154 }
155 Glib::signal_timeout().connect(
156 sigc::bind_return(
157 sigc::bind(
158 sigc::mem_fun(measure, &MeasureThreadsafe::print),
159 verbose),
160 true),
161 1000);
162 }
163
164 #endif
165
166
167 /****************************************************************
168 ** class SkinHandling
169 */
170
set_styledir(const string & style_dir)171 void SkinHandling::set_styledir(const string& style_dir) {
172 // fetch all skin names in directory
173 DIR *d;
174 d = opendir(style_dir.c_str());
175 if (!d) {
176 return;
177 }
178 // look for gx_head_*.css and extract *-part
179 struct dirent *de;
180 skin_list.clear();
181 while ((de = readdir(d)) != 0) {
182 char *p = de->d_name;
183 if (strncmp(p, "gx_head_", 8) != 0) {
184 continue;
185 }
186 if (strncmp(p, "gx_head_gx", 10) == 0) {
187 continue;
188 }
189 p += 8;
190 int n = strlen(p) - 4;
191 if (strcmp(p+n, ".css") != 0) {
192 continue;
193 }
194 skin_list.push_back(string(p, n));
195 }
196 closedir(d);
197 sort(skin_list.begin(), skin_list.end());
198 }
199
get_cssfile() const200 string SkinHandling::get_cssfile() const {
201 if (name.empty()) {
202 return "minimal.css";
203 }
204 return "gx_head_" + name + ".css";
205 }
206
is_in_list(const string & name)207 bool SkinHandling::is_in_list(const string& name) {
208 return index(name) < skin_list.size();
209 }
210
index(const Glib::ustring & name)211 unsigned int SkinHandling::index(const Glib::ustring& name) {
212 unsigned int i = 0;
213 for (; i < skin_list.size(); ++i) {
214 if (skin_list[i] == name) {
215 break;
216 }
217 }
218 return i;
219 }
220
operator [](unsigned int idx)221 const Glib::ustring& SkinHandling::operator[](unsigned int idx) {
222 if (idx < skin_list.size()) {
223 return skin_list[idx];
224 } else {
225 static Glib::ustring empty;
226 return empty;
227 }
228 }
229
set_default_skin_name()230 void SkinHandling::set_default_skin_name() {
231 name = "Guitarix";
232 }
233
234 /****************************************************************
235 ** class PathList
236 */
237
PathList(const char * env_name)238 PathList::PathList(const char *env_name): dirs() {
239 if (!env_name) {
240 return;
241 }
242 const char *p = getenv(env_name);
243 if (!p) {
244 return;
245 }
246 while (true) {
247 const char *q = strchr(p, ':');
248 if (q) {
249 int n = q - p;
250 if (n) {
251 add(std::string(p, n));
252 }
253 p = q+1;
254 } else {
255 if (*p) {
256 add(p);
257 }
258 break;
259 }
260 }
261 }
262
contains(const string & d) const263 bool PathList::contains(const string& d) const {
264 Glib::RefPtr<Gio::File> f = Gio::File::create_for_path(d);
265 for (pathlist::const_iterator i = dirs.begin();
266 i != dirs.end(); ++i) {
267 if (f->equal(*i)) {
268 return true;
269 }
270 }
271 return false;
272 }
273
274
find_dir(std::string * d,const std::string & filename) const275 bool PathList::find_dir(std::string* d, const std::string& filename) const {
276 for (pathlist::const_iterator i = dirs.begin();
277 i != dirs.end(); ++i) {
278 string p = (*i)->get_path();
279 string fn = Glib::build_filename(p, filename);
280 if (access(fn.c_str(), R_OK) == 0) {
281 *d = p;
282 return true;
283 }
284 }
285 return false;
286 }
287
288
289 /****************************************************************
290 ** class PrefixConverter
291 */
292
add(char s,const std::string & d)293 void PrefixConverter::add(char s, const std::string& d) {
294 assert(s != '%');
295 dirs[s] = (d[d.size()-1] == '/' ? d.substr(0,d.size()-1) : d);
296 }
297
replace_symbol(const std::string & dir) const298 std::string PrefixConverter::replace_symbol(const std::string& dir) const {
299 if (dir.size() < 2 || dir[0] != '%') {
300 return dir;
301 }
302 symbol_path_map::const_iterator i = dirs.find(dir[1]);
303 if (i != dirs.end()) {
304 return Glib::build_filename(i->second, dir.substr(2));
305 }
306 if (dir.compare(0, 2, "%%")) {
307 return dir.substr(1);
308 }
309 return dir;
310 }
311
replace_path(const std::string & dir) const312 std::string PrefixConverter::replace_path(const std::string& dir) const {
313 for (symbol_path_map::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
314 size_t n = i->second.size();
315 if (dir.compare(0, n, i->second) == 0) {
316 std::string tail = dir.substr(n);
317 if (Glib::build_filename(i->second, tail) == dir) {
318 std::string sym = "%";
319 sym.push_back(i->first);
320 return sym + tail;
321 }
322 }
323 }
324 if (dir.size() < 2 || dir[0] != '%') {
325 return dir;
326 }
327 return "%" + dir;
328 }
329
330
331 /*****************************************************************
332 ** class DirectoryListing
333 */
334
IRFileListing(const std::string & path)335 IRFileListing::IRFileListing(const std::string& path) {
336 Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(path);
337 if (file->query_exists()) {
338 Glib::RefPtr<Gio::FileEnumerator> child_enumeration =
339 file->enumerate_children(G_FILE_ATTRIBUTE_STANDARD_NAME
340 "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
341 "," G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
342 Glib::RefPtr<Gio::FileInfo> file_info;
343 while ((file_info = child_enumeration->next_file())) {
344 std::string content_type = file_info->get_attribute_string(
345 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
346 if (content_type.substr(0, 6) == "audio/") {
347 listing.push_back(
348 FileName(
349 file_info->get_attribute_byte_string(G_FILE_ATTRIBUTE_STANDARD_NAME),
350 file_info->get_attribute_string(G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)));
351 }
352 }
353 } else {
354 gx_print_error(
355 "jconvolver",
356 boost::format(_("Error reading file path %1%")) % path);
357 }
358 }
359
list_subdirs(const Glib::RefPtr<Gio::File> & file,std::vector<FileName> & dirs,const Glib::ustring & prefix)360 static void list_subdirs(const Glib::RefPtr<Gio::File>& file, std::vector<FileName>& dirs, const Glib::ustring& prefix) {
361 Glib::RefPtr<Gio::FileEnumerator> child_enumeration =
362 file->enumerate_children(G_FILE_ATTRIBUTE_STANDARD_NAME
363 "," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
364 Glib::RefPtr<Gio::FileInfo> file_info;
365 while ((file_info = child_enumeration->next_file())) {
366 if (file_info->get_file_type() == Gio::FILE_TYPE_DIRECTORY) {
367 Glib::RefPtr<Gio::File> child = file->get_child(
368 file_info->get_attribute_byte_string(G_FILE_ATTRIBUTE_STANDARD_NAME));
369 dirs.push_back(
370 FileName(
371 child->get_path(),
372 prefix+file_info->get_attribute_string(G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)));
373 list_subdirs(child, dirs, prefix+" ");
374 }
375 }
376 }
377
list_subdirs(PathList pl,std::vector<FileName> & dirs)378 void list_subdirs(PathList pl, std::vector<FileName>& dirs) {
379 for (PathList::iterator i = pl.begin(); i != pl.end(); ++i) {
380 std::string fn = (*i)->get_path();
381 dirs.push_back(FileName(fn, fn));
382 list_subdirs(*i, dirs, " ");
383 }
384 }
385
386
387 /****************************************************************
388 ** class BasicOptions
389 */
390
391 BasicOptions *BasicOptions::instance = 0;
392
BasicOptions()393 BasicOptions::BasicOptions()
394 : user_dir(),
395 user_IR_dir(),
396 sys_IR_dir(GX_SOUND_DIR),
397 IR_pathlist(),
398 IR_prefixmap(),
399 builder_dir(GX_BUILDER_DIR) {
400 user_dir = Glib::build_filename(Glib::get_user_config_dir(), "guitarix");
401 user_IR_dir = Glib::build_filename(user_dir, "IR");
402
403 make_ending_slash(user_dir);
404 make_ending_slash(user_IR_dir);
405 make_ending_slash(sys_IR_dir);
406 make_ending_slash(builder_dir);
407
408 // for legacy presets
409 IR_pathlist.add(get_user_IR_dir());
410 IR_pathlist.add(get_sys_IR_dir());
411
412 // for current presets
413 IR_prefixmap.add('U', get_user_IR_dir());
414 IR_prefixmap.add('S', get_sys_IR_dir());
415
416 instance = this;
417 }
418
~BasicOptions()419 BasicOptions::~BasicOptions() {
420 instance = 0;
421 }
422
make_ending_slash(string & dirpath)423 void BasicOptions::make_ending_slash(string& dirpath) {
424 if (dirpath.empty()) {
425 return;
426 }
427 if (dirpath[dirpath.size()-1] != '/') {
428 dirpath += "/";
429 }
430 }
431
432
433 /****************************************************************
434 ** class CmdlineOptions
435 */
436
shellvar(const char * name)437 static inline const char *shellvar(const char *name) {
438 const char *p = getenv(name);
439 return p ? p : "";
440 }
441
442 #define TCLR(s) "\033[1;32m" s "\033[0m" // light green
443 #define TCLR2(s) TCLR(s), s
444
CmdlineOptions()445 CmdlineOptions::CmdlineOptions()
446 : BasicOptions(),
447 main_group("",""),
448 optgroup_style("style", TCLR2("GTK style configuration options")),
449 optgroup_jack("jack", TCLR2("JACK configuration options")),
450 optgroup_overload("overload", TCLR2("Switch to bypass mode on overload condition")),
451 optgroup_file("file", TCLR2("File options")),
452 optgroup_debug("debug", TCLR2("Debug options")),
453 version(false), clear(false),
454 jack_input(shellvar("GUITARIX2JACK_INPUTS")),
455 jack_midi(shellvar("GUITARIX2JACK_MIDI")),
456 jack_outputs(),
457 jack_uuid(),
458 jack_uuid2(),
459 jack_noconnect(false),
460 jack_single(false),
461 jack_servername(),
462 load_file(shellvar("GUITARIX_LOAD_FILE")),
463 style_dir(GX_STYLE_DIR),
464 factory_dir(GX_FACTORY_DIR),
465 pixmap_dir(GX_PIXMAPS_DIR),
466 old_user_dir(),
467 preset_dir(),
468 pluginpreset_dir(),
469 lv2_preset_dir(),
470 temp_dir(),
471 plugin_dir(),
472 loop_dir(),
473 rcset(shellvar("GUITARIX_RC_STYLE")),
474 nogui(false),
475 rpcport(RPCPORT_DEFAULT),
476 rpcaddress(),
477 onlygui(false),
478 liveplaygui(false),
479 hideonquit(false),
480 mute(false),
481 setbank(),
482 cmdline_bank(),
483 cmdline_preset(),
484 tuner_tet(),
485 tuner_ref(),
486 sporadic_overload(0),
487 idle_thread_timeout(0),
488 convolver_watchdog(true),
489 watchdog_warning(true),
490 xrun_watchdog(false),
491 lterminal(false),
492 a_save(false),
493 auto_save(false),
494 #ifndef NDEBUG
495 dump_parameter(false),
496 #endif
497 skin(style_dir),
498 mainwin_visible(0),
499 mainwin_x(-1),
500 mainwin_y(-1),
501 mainwin_height(-1),
502 window_height(600),
503 preset_window_height(220),
504 mul_buffer(1),
505 no_warn_latency(false),
506 system_order_rack_h(false),
507 system_show_value(false),
508 system_show_tooltips(true),
509 system_animations(true),
510 system_show_presets(false),
511 system_show_toolbar(false),
512 system_show_rack(false),
513 system_midiout(false),
514 reload_lv2_presets(true) {
515 const char* home = getenv("HOME");
516 if (!home) {
517 throw GxFatalError(_("no HOME environment variable"));
518 }
519 old_user_dir = string(home) + "/.gx_head/";
520 plugin_dir = Glib::build_filename(get_user_dir(), "plugins");
521 preset_dir = Glib::build_filename(get_user_dir(), "banks");
522 pluginpreset_dir = Glib::build_filename(get_user_dir(), "pluginpresets");
523 lv2_preset_dir = Glib::build_filename(get_user_dir(), "pluginpresets/lv2");
524 loop_dir = Glib::build_filename(get_pluginpreset_dir(), "loops");
525 temp_dir = Glib::build_filename(get_user_dir(), "temp");
526 const char *tmp = getenv("GUITARIX2JACK_OUTPUTS1");
527 if (tmp && *tmp) {
528 jack_outputs.push_back(tmp);
529 }
530 tmp = getenv("GUITARIX2JACK_OUTPUTS2");
531 if (tmp && *tmp) {
532 jack_outputs.push_back(tmp);
533 }
534
535 const char *nsm_url = getenv( "NSM_URL" );
536
537 if (!nsm_url) {
538 read_ui_vars();
539 }
540
541 // ---- parse command line arguments
542 set_summary(
543 "All parameters are optional. Examples:\n"
544 "\tguitarix\n"
545 "\tguitarix -r gx4-black -i system:capture_3\n"
546 "\tguitarix -c -o system:playback_1 -o system:playback_2");
547
548 // main group
549 Glib::OptionEntry opt_version;
550 opt_version.set_short_name('v');
551 opt_version.set_long_name("version");
552 opt_version.set_description("Print version string and exit");
553 Glib::OptionEntry opt_nogui;
554 opt_nogui.set_short_name('N');
555 opt_nogui.set_long_name("nogui");
556 opt_nogui.set_description("start without GUI");
557 Glib::OptionEntry opt_rpcport;
558 opt_rpcport.set_short_name('p');
559 opt_rpcport.set_long_name("rpcport");
560 opt_rpcport.set_description("start a JSON-RPC server listening on port PORT");
561 opt_rpcport.set_arg_description("PORT");
562 Glib::OptionEntry opt_rpchost;
563 opt_rpchost.set_short_name('H');
564 opt_rpchost.set_long_name("rpchost");
565 opt_rpchost.set_description("set hostname to connect to");
566 opt_rpchost.set_arg_description("HOSTNAME");
567 Glib::OptionEntry opt_onlygui;
568 opt_onlygui.set_short_name('G');
569 opt_onlygui.set_long_name("onlygui");
570 opt_onlygui.set_description("start only GUI");
571 Glib::OptionEntry opt_liveplaygui;
572 opt_liveplaygui.set_short_name('L');
573 opt_liveplaygui.set_long_name("liveplaygui");
574 opt_liveplaygui.set_description("start with Live Play GUI");
575 Glib::OptionEntry opt_hideonquit;
576 opt_hideonquit.set_short_name('E');
577 opt_hideonquit.set_long_name("hideonquit");
578 opt_hideonquit.set_description("only hide GUI instead quit engine");
579 Glib::OptionEntry opt_mute;
580 opt_mute.set_short_name('M');
581 opt_mute.set_long_name("mute");
582 opt_mute.set_description("start with engine muted");
583 Glib::OptionEntry opt_bank;
584 opt_bank.set_short_name('b');
585 opt_bank.set_long_name("bank");
586 opt_bank.set_description("set bank and preset to load at startup");
587 opt_bank.set_arg_description("BANK:PRESET (A:0-Z:9)");
588 Glib::OptionEntry opt_tuner_tet;
589 opt_tuner_tet.set_short_name('t');
590 opt_tuner_tet.set_long_name("tuner_tet");
591 opt_tuner_tet.set_description("set tuner temperament at startup");
592 opt_tuner_tet.set_arg_description("tuner temperament (12, 19, 24, 31, 41, 53)");
593 Glib::OptionEntry opt_tuner_ref;
594 opt_tuner_ref.set_short_name('F');
595 opt_tuner_ref.set_long_name("reference_pitch");
596 opt_tuner_ref.set_description("set tuner reference pitch at startup");
597 opt_tuner_ref.set_arg_description("tuner reference pitch (225 - 453)");
598 Glib::OptionEntry opt_tuner_feedback;
599 opt_tuner_feedback.set_long_name("tuner-midi-feedback");
600 opt_tuner_feedback.set_description("send tuner midi feedback");
601 main_group.add_entry(opt_version, version);
602 main_group.add_entry(opt_nogui, nogui);
603 main_group.add_entry(opt_rpcport, rpcport);
604 main_group.add_entry(opt_rpchost, rpcaddress);
605 main_group.add_entry(opt_onlygui, onlygui);
606 main_group.add_entry(opt_liveplaygui, liveplaygui);
607 main_group.add_entry(opt_hideonquit, hideonquit);
608 main_group.add_entry(opt_mute, mute);
609 main_group.add_entry(opt_bank, setbank);
610 main_group.add_entry(opt_tuner_tet, tuner_tet);
611 main_group.add_entry(opt_tuner_ref, tuner_ref);
612 main_group.add_entry(opt_tuner_feedback, system_tuner_midiout);
613 set_main_group(main_group);
614
615 // style options
616 Glib::OptionEntry opt_clear;
617 opt_clear.set_short_name('c');
618 opt_clear.set_long_name("clear");
619 opt_clear.set_description("Use 'default' GTK style");
620 Glib::OptionEntry opt_rcset;
621 opt_rcset.set_short_name('r');
622 opt_rcset.set_long_name("rcset");
623 opt_rcset.set_description(get_opskin());
624 opt_rcset.set_arg_description("STYLE");
625 optgroup_style.add_entry(opt_clear, clear);
626 optgroup_style.add_entry(opt_rcset, rcset);
627
628 // JACK options
629 Glib::OptionEntry opt_jack_input;
630 opt_jack_input.set_short_name('i');
631 opt_jack_input.set_long_name("jack-input");
632 opt_jack_input.set_description("Guitarix JACK input");
633 opt_jack_input.set_arg_description("PORT");
634 Glib::OptionEntry opt_jack_output;
635 opt_jack_output.set_short_name('o');
636 opt_jack_output.set_long_name("jack-output");
637 opt_jack_output.set_description("Guitarix JACK outputs");
638 opt_jack_output.set_arg_description("PORT");
639 Glib::OptionEntry opt_jack_midi;
640 opt_jack_midi.set_short_name('m');
641 opt_jack_midi.set_long_name("jack-midi");
642 opt_jack_midi.set_description("Guitarix JACK midi control");
643 opt_jack_midi.set_arg_description("PORT");
644 Glib::OptionEntry opt_jack_midi_feedback;
645 opt_jack_midi_feedback.set_long_name("jack-midi-feedback");
646 opt_jack_midi_feedback.set_description("Guitarix send JACK midi feedback");
647 Glib::OptionEntry opt_jack_noconnect;
648 opt_jack_noconnect.set_short_name('J');
649 opt_jack_noconnect.set_long_name("jack-no-connect");
650 opt_jack_noconnect.set_description("disable self-connect JACK ports");
651 Glib::OptionEntry opt_jack_instance;
652 opt_jack_instance.set_short_name('n');
653 opt_jack_instance.set_long_name("name");
654 opt_jack_instance.set_description("instance name (default gx_head)");
655 opt_jack_instance.set_arg_description("NAME");
656 Glib::OptionEntry opt_jack_single;
657 opt_jack_single.set_short_name('D');
658 opt_jack_single.set_long_name("disable-multi-client");
659 opt_jack_single.set_description("run guitarix as single client");
660 Glib::OptionEntry opt_jack_uuid;
661 opt_jack_uuid.set_short_name('U');
662 opt_jack_uuid.set_long_name("jack-uuid");
663 opt_jack_uuid.set_description("JackSession ID");
664 opt_jack_uuid.set_arg_description("UUID");
665 Glib::OptionEntry opt_jack_uuid2;
666 opt_jack_uuid2.set_short_name('A');
667 opt_jack_uuid2.set_long_name("jack-uuid2");
668 opt_jack_uuid2.set_description("JackSession ID");
669 opt_jack_uuid2.set_arg_description("UUID2");
670 Glib::OptionEntry opt_jack_servername;
671 opt_jack_servername.set_short_name('s');
672 opt_jack_servername.set_long_name("server-name");
673 opt_jack_servername.set_description("JACK server name to connect to");
674 opt_jack_servername.set_arg_description("NAME");
675 optgroup_jack.add_entry(opt_jack_input, jack_input);
676 optgroup_jack.add_entry(opt_jack_output, jack_outputs);
677 optgroup_jack.add_entry(opt_jack_midi, jack_midi);
678 optgroup_jack.add_entry(opt_jack_midi_feedback, system_midiout);
679 optgroup_jack.add_entry(opt_jack_noconnect, jack_noconnect);
680 optgroup_jack.add_entry(opt_jack_instance, jack_instance);
681 optgroup_jack.add_entry(opt_jack_single, jack_single);
682 optgroup_jack.add_entry(opt_jack_uuid, jack_uuid);
683 optgroup_jack.add_entry(opt_jack_uuid2, jack_uuid2);
684 optgroup_jack.add_entry(opt_jack_servername, jack_servername);
685
686 // Engine overload options
687 Glib::OptionEntry opt_watchdog_idle;
688 opt_watchdog_idle.set_short_name('I');
689 opt_watchdog_idle.set_long_name("idle-timeout");
690 opt_watchdog_idle.set_description(
691 "starved idle thread probe (default: disabled)");
692 opt_watchdog_idle.set_arg_description("SECONDS");
693 Glib::OptionEntry opt_watchdog_convolver;
694 opt_watchdog_convolver.set_short_name('C');
695 opt_watchdog_convolver.set_long_name("no-convolver-overload");
696 opt_watchdog_convolver.set_description(
697 "disable overload on convolver missed deadline");
698 opt_watchdog_convolver.set_flags(Glib::OptionEntry::FLAG_REVERSE);
699 Glib::OptionEntry opt_watchdog_warning;
700 opt_watchdog_warning.set_short_name('W');
701 opt_watchdog_warning.set_long_name("no-watchdog-warning");
702 opt_watchdog_warning.set_description(
703 "do not pop-up warning for bypassed overload condition");
704 opt_watchdog_warning.set_flags(Glib::OptionEntry::FLAG_REVERSE);
705 Glib::OptionEntry opt_watchdog_xrun;
706 opt_watchdog_xrun.set_short_name('X');
707 opt_watchdog_xrun.set_long_name("xrun-overload");
708 opt_watchdog_xrun.set_description(
709 "JACK xrun (default: false)");
710 Glib::OptionEntry opt_sporadic_overload;
711 opt_sporadic_overload.set_short_name('S');
712 opt_sporadic_overload.set_long_name("sporadic");
713 opt_sporadic_overload.set_description(
714 "allow single overload events per interval (default: disabled)");
715 opt_sporadic_overload.set_arg_description("SECONDS");
716 optgroup_overload.add_entry(opt_watchdog_idle, idle_thread_timeout);
717 optgroup_overload.add_entry(opt_watchdog_convolver, convolver_watchdog);
718 optgroup_overload.add_entry(opt_watchdog_warning, watchdog_warning);
719 optgroup_overload.add_entry(opt_watchdog_xrun, xrun_watchdog);
720 optgroup_overload.add_entry(opt_sporadic_overload, sporadic_overload);
721
722 // FILE options
723 Glib::OptionEntry opt_load_file;
724 opt_load_file.set_short_name('f');
725 opt_load_file.set_long_name("load-file");
726 opt_load_file.set_description(_("load state file on startup"));
727 opt_load_file.set_arg_description("FILE");
728 optgroup_file.add_entry_filename(opt_load_file, load_file);
729 Glib::OptionEntry opt_plugin_dir;
730 opt_plugin_dir.set_short_name('P');
731 opt_plugin_dir.set_long_name("plugin-dir");
732 opt_plugin_dir.set_description(_("directory with guitarix plugins (.so files)"));
733 opt_plugin_dir.set_arg_description("DIR");
734 optgroup_file.add_entry_filename(opt_plugin_dir, plugin_dir);
735 Glib::OptionEntry opt_save_on_exit;
736 opt_save_on_exit.set_short_name('K');
737 opt_save_on_exit.set_long_name("disable-save-on-exit");
738 opt_save_on_exit.set_description(_("disable auto save to state file when quit"));
739 optgroup_file.add_entry(opt_save_on_exit, a_save);
740 Glib::OptionEntry opt_auto_save;
741 opt_auto_save.set_short_name('a');
742 opt_auto_save.set_long_name("auto-save");
743 opt_auto_save.set_description(_("enable auto save (only in server mode)"));
744 optgroup_file.add_entry(opt_auto_save, auto_save);
745
746 // DEBUG options
747 Glib::OptionEntry opt_builder_dir;
748 opt_builder_dir.set_short_name('B');
749 opt_builder_dir.set_long_name("builder-dir");
750 opt_builder_dir.set_description(_("directory from which .glade files are loaded"));
751 opt_builder_dir.set_arg_description("DIR");
752 optgroup_debug.add_entry_filename(opt_builder_dir, builder_dir);
753 Glib::OptionEntry opt_style_dir;
754 opt_style_dir.set_short_name('S');
755 opt_style_dir.set_long_name("style-dir");
756 opt_style_dir.set_description(_("directory with skin style definitions (.css files)"));
757 opt_style_dir.set_arg_description("DIR");
758 optgroup_debug.add_entry_filename(opt_style_dir, style_dir);
759 Glib::OptionEntry opt_log_terminal;
760 opt_log_terminal.set_short_name('t');
761 opt_log_terminal.set_long_name("log-terminal");
762 opt_log_terminal.set_description(_("print log on terminal"));
763 optgroup_debug.add_entry(opt_log_terminal, lterminal);
764 #ifndef NDEBUG
765 Glib::OptionEntry opt_dump_parameter;
766 opt_dump_parameter.set_short_name('d');
767 opt_dump_parameter.set_long_name("dump-parameter");
768 opt_dump_parameter.set_description(_("dump parameter table in json format"));
769 optgroup_debug.add_entry(opt_dump_parameter, dump_parameter);
770 #endif
771
772 // collecting all option groups
773 add_group(optgroup_style);
774 add_group(optgroup_jack);
775 add_group(optgroup_overload);
776 add_group(optgroup_file);
777 add_group(optgroup_debug);
778 }
779
~CmdlineOptions()780 CmdlineOptions::~CmdlineOptions() {
781 write_ui_vars();
782 }
783
read_ui_vars()784 void CmdlineOptions::read_ui_vars() {
785 ifstream i(Glib::build_filename(get_user_dir(), "ui_rc").c_str());
786 if (i.fail()) {
787 return;
788 }
789 JsonParser jp(&i);
790 try {
791 jp.next(JsonParser::begin_object);
792 while (jp.peek() != JsonParser::end_object) {
793 jp.next(JsonParser::value_key);
794 if (jp.current_value() == "system.mainwin_visible") {
795 jp.next(JsonParser::value_number);
796 mainwin_visible = jp.current_value_int();
797 } else if (jp.current_value() == "system.mainwin_x") {
798 jp.next(JsonParser::value_number);
799 mainwin_x = jp.current_value_int();
800 } else if (jp.current_value() == "system.mainwin_y") {
801 jp.next(JsonParser::value_number);
802 mainwin_y = jp.current_value_int();
803 } else if (jp.current_value() == "system.mainwin_height") {
804 jp.next(JsonParser::value_number);
805 mainwin_height = jp.current_value_int();
806 } else if (jp.current_value() == "system.mainwin_rack_height") {
807 jp.next(JsonParser::value_number);
808 window_height = jp.current_value_int();
809 } else if (jp.current_value() == "system.preset_window_height") {
810 jp.next(JsonParser::value_number);
811 preset_window_height = jp.current_value_int();
812 } else if (jp.current_value() == "system.mul_buffer") {
813 jp.next(JsonParser::value_number);
814 mul_buffer = jp.current_value_int();
815 } else if (jp.current_value() == "ui.skin_name") {
816 jp.next(JsonParser::value_string);
817 if (skin.is_in_list(jp.current_value())) {
818 skin.name = jp.current_value();
819 } else {
820 gx_print_error(
821 _("load state"),
822 Glib::ustring::compose(_("Skin '%1' not found"), jp.current_value()));
823 }
824 } else if (jp.current_value() == "ui.latency_nowarn") {
825 jp.next(JsonParser::value_number);
826 no_warn_latency = jp.current_value_int();
827 } else if (jp.current_value() == "system.order_rack_h") {
828 jp.next(JsonParser::value_number);
829 system_order_rack_h = jp.current_value_int();
830 } else if (jp.current_value() == "system.show_value") {
831 jp.next(JsonParser::value_number);
832 system_show_value = jp.current_value_int();
833 } else if (jp.current_value() == "system.show_tooltips") {
834 jp.next(JsonParser::value_number);
835 system_show_tooltips = jp.current_value_int();
836 } else if (jp.current_value() == "system.animations") {
837 jp.next(JsonParser::value_number);
838 system_animations = jp.current_value_int();
839 } else if (jp.current_value() == "system.show_presets") {
840 jp.next(JsonParser::value_number);
841 system_show_presets = jp.current_value_int();
842 } else if (jp.current_value() == "system.show_toolbar") {
843 jp.next(JsonParser::value_number);
844 system_show_toolbar = jp.current_value_int();
845 } else if (jp.current_value() == "system.show_rack") {
846 jp.next(JsonParser::value_number);
847 system_show_rack = jp.current_value_int();
848 } else if (jp.current_value() == "system.midiout") {
849 jp.next(JsonParser::value_number);
850 system_midiout = jp.current_value_int();
851 }
852 }
853 jp.next(JsonParser::end_object);
854 jp.close();
855 } catch (JsonException&) {
856 gx_print_warning("main", "can't read/parse ui_rc");
857 }
858 i.close();
859 }
860
write_ui_vars()861 void CmdlineOptions::write_ui_vars() {
862 ofstream o(Glib::build_filename(get_user_dir(), "ui_rc").c_str());
863 if (o.fail()) {
864 return;
865 }
866 JsonWriter jw(&o);
867 try {
868 jw.begin_object(true);
869 jw.write_kv("system.mainwin_visible", mainwin_visible);
870 jw.write_kv("system.mainwin_x", mainwin_x);
871 jw.write_kv("system.mainwin_y", mainwin_y);
872 jw.write_kv("system.mainwin_height", mainwin_height);
873 jw.write_kv("system.mainwin_rack_height", window_height);
874 jw.write_kv("system.preset_window_height", preset_window_height);
875 jw.write_kv("system.mul_buffer", mul_buffer);
876 jw.write_kv("ui.skin_name", skin.name);
877 jw.write_kv("ui.latency_nowarn", no_warn_latency);
878 jw.write_kv("system.order_rack_h", system_order_rack_h);
879 jw.write_kv("system.show_value", system_show_value);
880 jw.write_kv("system.show_tooltips", system_show_tooltips);
881 jw.write_kv("system.animations", system_animations);
882 jw.write_kv("system.show_presets", system_show_presets);
883 jw.write_kv("system.show_toolbar", system_show_toolbar);
884 jw.write_kv("system.show_rack", system_show_rack);
885 jw.write_kv("system.midiout", system_midiout);
886 jw.end_object(true);
887 jw.close();
888 } catch (JsonException&) {
889 gx_print_warning("main", "can't write ui_rc");
890 }
891 o.close();
892 }
893
get_jack_output(unsigned int n) const894 Glib::ustring CmdlineOptions::get_jack_output(unsigned int n) const {
895 if (n >= jack_outputs.size()) {
896 return "";
897 }
898 return jack_outputs.at(n);
899 }
900
get_opskin()901 string CmdlineOptions::get_opskin() {
902 // GTK options: rc style (aka skin)
903 string opskin("Style to use");
904
905 // initialize number of skins. We just count the number of rc files
906 unsigned int n = skin.skin_list.size();
907 if (n < 1) {
908 return opskin;
909 }
910
911 vector<Glib::ustring>::iterator it;
912
913 for (it = skin.skin_list.begin(); it != skin.skin_list.end(); ++it) {
914 opskin += ", '" + *it + "'";
915 }
916 return opskin;
917 }
918
log_terminal(const string & msg,GxLogger::MsgType tp,bool plugged)919 static void log_terminal(const string& msg, GxLogger::MsgType tp, bool plugged) {
920 const char *t;
921 switch (tp) {
922 case GxLogger::kInfo: t = "I"; break;
923 case GxLogger::kWarning: t = "W"; break;
924 case GxLogger::kError: t = "E"; break;
925 default: t = "?"; break;
926 }
927 if (!plugged) {
928 cerr << t << " " << msg << endl;
929 }
930 }
931
process(int argc,char ** argv)932 void CmdlineOptions::process(int argc, char** argv) {
933 path_to_program = Gio::File::create_for_path(argv[0])->get_path();
934 if (version) {
935 std::cout << "Guitarix version \033[1;32m"
936 << GX_VERSION << endl
937 << "\033[0m Copyright " << static_cast<char>(0x40) << " 2010 "
938 << "Hermman Meyer - James Warden - Andreas Degert"
939 << endl;
940 exit(0);
941 }
942 #ifdef NDEBUG
943 if (argc > 1) {
944 throw GxFatalError(
945 string("unknown argument on command line: ")+argv[1]);
946 }
947 #endif
948 if (clear) {
949 if (!rcset.empty()) {
950 throw Glib::OptionError(
951 Glib::OptionError::BAD_VALUE,
952 _("-c and -r cannot be used together"));
953 }
954 skin.name = "";
955 } else if (skin.name.empty()) {
956 skin.set_default_skin_name();
957 }
958 if (nogui && liveplaygui) {
959 throw Glib::OptionError(
960 Glib::OptionError::BAD_VALUE,
961 _("-N and -L cannot be used together"));
962 }
963 if (onlygui && !setbank.empty()) {
964 throw Glib::OptionError(
965 Glib::OptionError::BAD_VALUE,
966 _("-G and -b cannot be used together"));
967 }
968 if (lterminal) {
969 GxLogger::get_logger().signal_message().connect(
970 sigc::ptr_fun(log_terminal));
971 if (nogui) {
972 GxLogger::get_logger().unplug_queue();
973 }
974 }
975
976 make_ending_slash(builder_dir);
977 make_ending_slash(style_dir);
978 make_ending_slash(factory_dir);
979 make_ending_slash(pixmap_dir);
980 make_ending_slash(preset_dir);
981 make_ending_slash(pluginpreset_dir);
982 make_ending_slash(lv2_preset_dir);
983 make_ending_slash(loop_dir);
984 make_ending_slash(temp_dir);
985 make_ending_slash(plugin_dir);
986
987 skin.set_styledir(style_dir);
988 unsigned int n = skin.skin_list.size();
989 if (n < 1) {
990 gx_print_fatal(_("main"), string(_("number of skins is 0")));
991 }
992 if (!rcset.empty()) {
993 if (skin.is_in_list(rcset)) {
994 skin.name = rcset;
995 } else {
996 throw Glib::OptionError(
997 Glib::OptionError::BAD_VALUE,
998 (boost::format(_("invalid style '%1%' on command line"))
999 % rcset).str());
1000 }
1001 }
1002 if (jack_outputs.size() > 2) {
1003 gx_print_warning(
1004 _("main"),
1005 _("Warning --> provided more than 2 output ports, ignoring extra ports"));
1006 }
1007 }
1008
1009
1010 /****************************************************************
1011 ** misc functions
1012 */
1013
1014 // ---- gx_head system function
gx_system_call(const string & cmd,const bool devnull,const bool escape)1015 int gx_system_call(const string& cmd,
1016 const bool devnull,
1017 const bool escape) {
1018 string str = cmd;
1019
1020 if (devnull)
1021 str.append(" 1>/dev/null 2>&1");
1022
1023 if (escape)
1024 str.append("&");
1025
1026 // cerr << " ********* \n system command = " << str.c_str() << endl;
1027
1028 sigset_t waitset;
1029 sigemptyset(&waitset);
1030 sigaddset(&waitset, SIGCHLD);
1031 sigprocmask(SIG_UNBLOCK, &waitset, NULL);
1032 int rc = system(str.c_str());
1033 sigprocmask(SIG_BLOCK, &waitset, NULL);
1034 return rc;
1035 }
1036
strip(Glib::ustring & s)1037 void strip(Glib::ustring& s) {
1038 size_t n = s.find_first_not_of(' ');
1039 if (n == Glib::ustring::npos) {
1040 s.erase();
1041 return;
1042 }
1043 if (n != 0) {
1044 s.erase(0, n);
1045 }
1046 s.erase(s.find_last_not_of(' ')+1);
1047 }
1048
1049 /*
1050 ** encoding / decoding filenames
1051 */
1052
check_char(unsigned char c)1053 static inline bool check_char(unsigned char c) {
1054 static const char *badchars = "/%?*<>\\:#&$'\"(){}[]~;`|.";
1055 if (c < 32) {
1056 return false;
1057 }
1058 for (const char *p = badchars; *p; p++) {
1059 if (c == *p) {
1060 return false;
1061 }
1062 }
1063 return true;
1064 }
1065
encode_filename(const std::string & s)1066 std::string encode_filename(const std::string& s) {
1067 std::string res;
1068 res.reserve(s.size());
1069 for (unsigned int i = 0; i < s.size(); i++) {
1070 unsigned char c = s[i];
1071 if (!check_char(c)) {
1072 res.append(1, '%');
1073 static const unsigned char code[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1074 res.append(1, code[c / 16]);
1075 res.append(1, code[c % 16]);
1076 } else {
1077 res.append(1, c);
1078 }
1079 }
1080 return res;
1081 }
1082
dct(unsigned char c,int & n)1083 static inline bool dct(unsigned char c, int &n) {
1084 if (c < '0') {
1085 return false;
1086 }
1087 if (c <= '9') {
1088 n = c - '0';
1089 return true;
1090 }
1091 if (c < 'a') {
1092 return false;
1093 }
1094 if (c <= 'f') {
1095 n = c - 'a';
1096 return true;
1097 }
1098 return false;
1099 }
1100
decode_filename(const std::string & s)1101 std::string decode_filename(const std::string& s) {
1102 std::string res;
1103 res.reserve(s.size());
1104 for (unsigned int i = 0; i < s.size(); i++) {
1105 unsigned char c = s[i];
1106 if (c == '%') {
1107 int n1, n2;
1108 if (s.size() - i < 3 || !dct(s[i+1],n1) || !dct(s[i+2],n2)) {
1109 // error, don't do any decoding
1110 return s;
1111 }
1112 res.push_back(n1*16 + n2);
1113 i += 2;
1114 } else {
1115 res.push_back(c);
1116 }
1117 }
1118 return res;
1119 }
1120
1121
1122 } /* end of gx_system namespace */
1123