2 Copyright (C) 2000-2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "libardour-config.h"
26 #include <cstdio> /* for sprintf */
34 #include <sys/types.h>
41 #include <glibmm/miscutils.h>
42 #include <glibmm/fileutils.h>
49 #include "pbd/error.h"
50 #include "pbd/stacktrace.h"
51 #include "pbd/xml++.h"
52 #include "pbd/basename.h"
53 #include "pbd/strsplit.h"
54 #include "pbd/replace_all.h"
56 #include "ardour/utils.h"
57 #include "ardour/rc_configuration.h"
61 using namespace ARDOUR;
66 legalize_for_path (const string& str)
68 string::size_type pos;
69 string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */
75 while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) {
76 legal.replace (pos, 1, "_");
80 return string (legal);
84 bump_name_once (const std::string& name, char delimiter)
86 string::size_type delim;
89 if ((delim = name.find_last_of (delimiter)) == string::npos) {
95 const char *last_element = name.c_str() + delim + 1;
96 for (size_t i = 0; i < strlen(last_element); i++) {
97 if (!isdigit(last_element[i])) {
104 int32_t version = strtol (name.c_str()+delim+1, (char **)NULL, 10);
106 if (isnumber == 0 || errno != 0) {
107 // last_element is not a number, or is too large
109 newname += delimiter;
114 snprintf (buf, sizeof(buf), "%d", version+1);
116 newname = name.substr (0, delim+1);
126 could_be_a_valid_path (const string& path)
128 vector<string> posix_dirs;
129 vector<string> dos_dirs;
132 split (path, posix_dirs, '/');
133 split (path, dos_dirs, '\\');
135 /* remove the last component of each */
137 posix_dirs.erase (--posix_dirs.end());
138 dos_dirs.erase (--dos_dirs.end());
140 if (G_DIR_SEPARATOR == '/') {
141 for (vector<string>::iterator x = posix_dirs.begin(); x != posix_dirs.end(); ++x) {
142 testpath = Glib::build_filename (testpath, *x);
143 cerr << "Testing " << testpath << endl;
144 if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
150 if (G_DIR_SEPARATOR == '\\') {
152 for (vector<string>::iterator x = dos_dirs.begin(); x != dos_dirs.end(); ++x) {
153 testpath = Glib::build_filename (testpath, *x);
154 cerr << "Testing " << testpath << endl;
155 if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) {
166 find_named_node (const XMLNode& node, string name)
169 XMLNodeConstIterator niter;
172 nlist = node.children();
174 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
178 if (child->name() == name) {
187 cmp_nocase (const string& s, const string& s2)
189 string::const_iterator p = s.begin();
190 string::const_iterator p2 = s2.begin();
192 while (p != s.end() && p2 != s2.end()) {
193 if (toupper(*p) != toupper(*p2)) {
194 return (toupper(*p) < toupper(*p2)) ? -1 : 1;
200 return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
204 touch_file (string path)
206 int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
215 region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
217 path = PBD::basename_nosuffix (path);
219 if (strip_channels) {
221 /* remove any "?R", "?L" or "?[a-z]" channel identifier */
223 string::size_type len = path.length();
225 if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
226 (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
228 path = path.substr (0, path.length() - 2);
232 if (add_channel_suffix) {
237 path += (char) ('a' + this_one);
239 path += (char) (this_one == 0 ? 'L' : 'R');
247 path_is_paired (string path, string& pair_base)
249 string::size_type pos;
251 /* remove any leading path */
253 if ((pos = path.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
254 path = path.substr(pos+1);
257 /* remove filename suffixes etc. */
259 if ((pos = path.find_last_of ('.')) != string::npos) {
260 path = path.substr (0, pos);
263 string::size_type len = path.length();
265 /* look for possible channel identifier: "?R", "%R", ".L" etc. */
267 if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
268 (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
270 pair_base = path.substr (0, len-2);
279 path_expand (string path)
286 /* Handle tilde and environment variable expansion in session path */
291 /* wordexp cannot be forced (it appears) into either
293 (1) NOT doing field splitting
294 (2) splitting based on something other than whitespace
296 (despite the documentation claiming that it obeys IFS etc).
298 so, quote the most likely spaces to occur in a path, and that should
299 be about as much as we can do.
303 replace_all (quoted, " ", "\\ ");
305 switch (wordexp (quoted.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF)) {
309 /* see docs on wordexp() */
310 wordfree (&expansion);
313 error << string_compose (_("illegal or badly-formed string used for path (%1)"), path) << endmsg;
317 if (expansion.we_wordc > 1) {
319 for (unsigned int i = 0; i < expansion.we_wordc; ++i) {
323 all += expansion.we_wordv[i];
325 error << string_compose (_("path (%1) is ambiguous: %2"), path, all) << endmsg;
329 ret = expansion.we_wordv[0];
331 wordfree (&expansion);
341 CFStringRefToStdString(CFStringRef stringRef)
344 CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
345 kCFStringEncodingUTF8);
346 char *buf = new char[size];
350 if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) {
359 compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
363 step = 1.0/(nframes-1);
367 for (framecnt_t i = 1; i < nframes - 1; ++i) {
368 in[i] = in[i-1] + step;
373 const float pan_law_attenuation = -3.0f;
374 const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
376 for (framecnt_t n = 0; n < nframes; ++n) {
378 float outVal = 1 - inVal;
379 out[n] = outVal * (scale * outVal + 1.0f - scale);
380 in[n] = inVal * (scale * inVal + 1.0f - scale);
385 string_to_edit_mode (string str)
387 if (str == _("Splice")) {
389 } else if (str == _("Slide")) {
391 } else if (str == _("Lock")) {
394 fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg;
400 edit_mode_to_string (EditMode mode)
416 string_to_sync_source (string str)
418 if (str == _("MIDI Timecode") || str == _("MTC")) {
422 if (str == _("MIDI Clock")) {
426 if (str == _("JACK")) {
430 fatal << string_compose (_("programming error: unknown sync source string \"%1\""), str) << endmsg;
435 /** @param sh Return a short version of the string */
437 sync_source_to_string (SyncSource src, bool sh)
447 return _("MIDI Timecode");
451 return _("MIDI Clock");
453 /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
458 meter_falloff_to_float (MeterFalloff falloff)
461 case MeterFalloffOff:
462 return METER_FALLOFF_OFF;
463 case MeterFalloffSlowest:
464 return METER_FALLOFF_SLOWEST;
465 case MeterFalloffSlow:
466 return METER_FALLOFF_SLOW;
467 case MeterFalloffMedium:
468 return METER_FALLOFF_MEDIUM;
469 case MeterFalloffFast:
470 return METER_FALLOFF_FAST;
471 case MeterFalloffFaster:
472 return METER_FALLOFF_FASTER;
473 case MeterFalloffFastest:
474 return METER_FALLOFF_FASTEST;
476 return METER_FALLOFF_FAST;
481 meter_falloff_from_float (float val)
483 if (val == METER_FALLOFF_OFF) {
484 return MeterFalloffOff;
486 else if (val <= METER_FALLOFF_SLOWEST) {
487 return MeterFalloffSlowest;
489 else if (val <= METER_FALLOFF_SLOW) {
490 return MeterFalloffSlow;
492 else if (val <= METER_FALLOFF_MEDIUM) {
493 return MeterFalloffMedium;
495 else if (val <= METER_FALLOFF_FAST) {
496 return MeterFalloffFast;
498 else if (val <= METER_FALLOFF_FASTER) {
499 return MeterFalloffFaster;
502 return MeterFalloffFastest;
507 ARDOUR::string_to_auto_state (std::string str)
509 if (str == X_("Off")) {
511 } else if (str == X_("Play")) {
513 } else if (str == X_("Write")) {
515 } else if (str == X_("Touch")) {
519 fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg;
525 ARDOUR::auto_state_to_string (AutoState as)
527 /* to be used only for XML serialization, no i18n done */
543 fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg;
549 ARDOUR::string_to_auto_style (std::string str)
551 if (str == X_("Absolute")) {
553 } else if (str == X_("Trim")) {
557 fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg;
563 ARDOUR::auto_style_to_string (AutoStyle as)
565 /* to be used only for XML serialization, no i18n done */
569 return X_("Absolute");
576 fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg;
582 bool_as_string (bool yn)
584 return (yn ? "yes" : "no");
588 string_is_affirmative (const std::string& str)
590 /* to be used only with XML data - not intended to handle user input */
596 /* the use of g_strncasecmp() is solely to get around issues with
597 * charsets posed by trying to use C++ for the same
598 * comparison. switching a std::string to its lower- or upper-case
599 * version has several issues, but handled by default
600 * in the way we desire when doing it in C.
603 return str == "1" || str == "y" || str == "Y" || (!g_strncasecmp(str.c_str(), "yes", str.length()));
607 native_header_format_extension (HeaderFormat hf, const DataType& type)
609 if (type == DataType::MIDI) {
630 fatal << string_compose (_("programming error: unknown native header format: %1"), hf);
636 matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
638 string bws = basename_nosuffix (path);
639 struct dirent* dentry;
644 if ((dead = ::opendir (dir.c_str())) == 0) {
645 error << string_compose (_("cannot open directory %1 (%2)"), dir, strerror (errno)) << endl;
649 while ((dentry = ::readdir (dead)) != 0) {
651 /* avoid '.' and '..' */
653 if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
654 (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
658 string fullpath = Glib::build_filename (dir, dentry->d_name);
660 if (::stat (fullpath.c_str(), &statbuf)) {
664 if (!S_ISREG (statbuf.st_mode)) {
668 string bws2 = basename_nosuffix (dentry->d_name);
681 how_many_dsp_threads ()
683 /* CALLER MUST HOLD PROCESS LOCK */
685 int num_cpu = hardware_concurrency();
686 int pu = Config->get_processor_usage ();
687 uint32_t num_threads = max (num_cpu - 1, 2); // default to number of cpus minus one, or 2, whichever is larger
690 /* pu is negative: use "pu" less cores for DSP than appear to be available
694 num_threads = num_cpu + pu;
697 } else if (pu == 0) {
699 /* use all available CPUs
702 num_threads = num_cpu;
705 /* use "pu" cores, if available
708 num_threads = min (num_cpu, pu);
714 double gain_to_slider_position_with_max (double g, double max_gain)
716 return gain_to_slider_position (g * 2.0/max_gain);
719 double slider_position_to_gain_with_max (double g, double max_gain)
721 return slider_position_to_gain (g * max_gain/2.0);
725 void c_stacktrace() { stacktrace (cerr); }