X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Futils.cc;h=d1d2372977c831cc4168240692347bdabc38bd79;hb=f6aaa1660bbce1782b8292d106c8adfb1ecbdc16;hp=64e06b6f12cac0bf1b3099eed7e8e64467cbbdd6;hpb=73192bc1a7ea55fa1864dc3826845b15c00dd2ec;p=ardour.git diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 64e06b6f12..d1d2372977 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -25,6 +25,8 @@ #include /* for sprintf */ #include +#include +#include #include #include #include @@ -34,22 +36,25 @@ #include #include #include +#ifndef COMPILER_MSVC #include +#endif #include +#include #include #include -#ifdef HAVE_WORDEXP -#include -#endif - +#include "pbd/cpus.h" #include "pbd/error.h" #include "pbd/stacktrace.h" #include "pbd/xml++.h" #include "pbd/basename.h" #include "pbd/strsplit.h" +#include "pbd/replace_all.h" + #include "ardour/utils.h" +#include "ardour/rc_configuration.h" #include "i18n.h" @@ -57,41 +62,99 @@ using namespace ARDOUR; using namespace std; using namespace PBD; -string -legalize_for_path (const string& str) +static string +replace_chars (const string& str, const string& illegal_chars) { -#if OLD_SCHOOL_PROHIBITIVE string::size_type pos; - string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: "; - string legal; + Glib::ustring legal; + + /* this is the one place in Ardour where we need to iterate across + * potential multibyte characters, and thus we need Glib::ustring + */ legal = str; pos = 0; - while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) { + while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) { legal.replace (pos, 1, "_"); pos += 1; } - return legal; -#else + return string (legal); +} +/** take an arbitrary string as an argument, and return a version of it + * suitable for use as a path (directory/folder name). This is the Ardour 3.X + * and later version of this code. It defines a very small number of characters + * that are not allowed in a path on the build target filesystem (basically, + * POSIX or Windows) and replaces any instances of them with an underscore. + * + * NOTE: this is intended only to legalize for the filesystem that Ardour + * is running on. Export should use legalize_for_universal_path() since + * the goal there is to be legal across filesystems. + */ +string +legalize_for_path (const string& str) +{ + return replace_chars (str, "/\\"); +} + +/** take an arbitrary string as an argument, and return a version of it + * suitable for use as a path (directory/folder name). This is the Ardour 3.X + * and later version of this code. It defines a small number + * of characters that are not allowed in a path on any of our target + * filesystems, and replaces any instances of them with an underscore. + * + * NOTE: this is intended to create paths that should be legal on + * ANY filesystem. + */ +string +legalize_for_universal_path (const string& str) +{ + return replace_chars (str, "<>:\"/\\|?*"); +} + +/** Legalize for a URI path component. This is like + * legalize_for_universal_path, but stricter, disallowing spaces and hash. + * This avoids %20 escapes in URIs, but probably needs work to be more strictly + * correct. + */ +string +legalize_for_uri (const string& str) +{ + return replace_chars (str, "<>:\"/\\|?* #"); +} + +/** take an arbitrary string as an argument, and return a version of it + * suitable for use as a path (directory/folder name). This is the Ardour 2.X + * version of this code, which used an approach that came to be seen as + * problematic: defining the characters that were allowed and replacing all + * others with underscores. See legalize_for_path() for the 3.X and later + * version. + */ + +string +legalize_for_path_2X (const string& str) +{ string::size_type pos; - string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */ - string legal; + string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: "; + Glib::ustring legal; + + /* this is the one place in Ardour where we need to iterate across + * potential multibyte characters, and thus we need Glib::ustring + */ legal = str; pos = 0; - while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) { + while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) { legal.replace (pos, 1, "_"); pos += 1; } - return legal; -#endif + return string (legal); } -string +string bump_name_once (const std::string& name, char delimiter) { string::size_type delim; @@ -133,46 +196,6 @@ bump_name_once (const std::string& name, char delimiter) } -bool -could_be_a_valid_path (const string& path) -{ - vector posix_dirs; - vector dos_dirs; - string testpath; - - split (path, posix_dirs, '/'); - split (path, dos_dirs, '\\'); - - /* remove the last component of each */ - - posix_dirs.erase (--posix_dirs.end()); - dos_dirs.erase (--dos_dirs.end()); - - if (G_DIR_SEPARATOR == '/') { - for (vector::iterator x = posix_dirs.begin(); x != posix_dirs.end(); ++x) { - testpath = Glib::build_filename (testpath, *x); - cerr << "Testing " << testpath << endl; - if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { - return false; - } - } - } - - if (G_DIR_SEPARATOR == '\\') { - testpath = ""; - for (vector::iterator x = dos_dirs.begin(); x != dos_dirs.end(); ++x) { - testpath = Glib::build_filename (testpath, *x); - cerr << "Testing " << testpath << endl; - if (!Glib::file_test (testpath, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { - return false; - } - } - } - - return true; -} - - XMLNode * find_named_node (const XMLNode& node, string name) { @@ -211,6 +234,41 @@ cmp_nocase (const string& s, const string& s2) return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1; } +int cmp_nocase_utf8 (const string& s1, const string& s2) +{ + const char *cstr1 = s1.c_str(); + const char *cstr2 = s2.c_str(); + gchar *cstr1folded = NULL; + gchar *cstr2folded = NULL; + int retval; + + if (!g_utf8_validate (cstr1, -1, NULL) || + !g_utf8_validate (cstr2, -1, NULL)) { + // fall back to comparing ASCII + return g_ascii_strcasecmp (cstr1, cstr2); + } + + cstr1folded = g_utf8_casefold (cstr1, -1); + cstr2folded = g_utf8_casefold (cstr2, -1); + + if (cstr1folded && cstr2folded) { + retval = strcmp (cstr1folded, cstr2folded); + } else { + // this shouldn't happen, make the best of it + retval = g_ascii_strcasecmp (cstr1, cstr2); + } + + if (cstr1folded) { + g_free (cstr1folded); + } + + if (cstr2folded) { + g_free (cstr2folded); + } + + return retval; +} + int touch_file (string path) { @@ -286,41 +344,6 @@ path_is_paired (string path, string& pair_base) return false; } -string -path_expand (string path) -{ - if (path.empty()) { - return path; - } - -#ifdef HAVE_WORDEXP - /* Handle tilde and environment variable expansion in session path */ - string ret = path; - - wordexp_t expansion; - switch (wordexp (path.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF)) { - case 0: - break; - default: - error << string_compose (_("illegal or badly-formed string used for path (%1)"), path) << endmsg; - goto out; - } - - if (expansion.we_wordc > 1) { - error << string_compose (_("path (%1) is ambiguous"), path) << endmsg; - goto out; - } - - ret = expansion.we_wordv[0]; - out: - wordfree (&expansion); - return ret; - -#else - return path; -#endif -} - #if __APPLE__ string CFStringRefToStdString(CFStringRef stringRef) @@ -400,7 +423,7 @@ edit_mode_to_string (EditMode mode) SyncSource string_to_sync_source (string str) { - if (str == _("MIDI Timecode")) { + if (str == _("MIDI Timecode") || str == _("MTC")) { return MTC; } @@ -409,26 +432,41 @@ string_to_sync_source (string str) } if (str == _("JACK")) { - return JACK; + return Engine; } fatal << string_compose (_("programming error: unknown sync source string \"%1\""), str) << endmsg; /*NOTREACHED*/ - return JACK; + return Engine; } +/** @param sh Return a short version of the string */ const char* -sync_source_to_string (SyncSource src) +sync_source_to_string (SyncSource src, bool sh) { switch (src) { - case JACK: + case Engine: + /* no other backends offer sync for now ... deal with this if we + * ever have to. + */ return _("JACK"); case MTC: - return _("MIDI Timecode"); + if (sh) { + return _("MTC"); + } else { + return _("MIDI Timecode"); + } case MIDIClock: - return _("MIDI Clock"); + if (sh) { + return _("M-Clock"); + } else { + return _("MIDI Clock"); + } + + case LTC: + return _("LTC"); } /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */ return _("JACK"); @@ -444,8 +482,12 @@ meter_falloff_to_float (MeterFalloff falloff) return METER_FALLOFF_SLOWEST; case MeterFalloffSlow: return METER_FALLOFF_SLOW; + case MeterFalloffSlowish: + return METER_FALLOFF_SLOWISH; case MeterFalloffMedium: return METER_FALLOFF_MEDIUM; + case MeterFalloffModerate: + return METER_FALLOFF_MODERATE; case MeterFalloffFast: return METER_FALLOFF_FAST; case MeterFalloffFaster: @@ -469,6 +511,12 @@ meter_falloff_from_float (float val) else if (val <= METER_FALLOFF_SLOW) { return MeterFalloffSlow; } + else if (val <= METER_FALLOFF_SLOWISH) { + return MeterFalloffSlowish; + } + else if (val <= METER_FALLOFF_MODERATE) { + return MeterFalloffModerate; + } else if (val <= METER_FALLOFF_MEDIUM) { return MeterFalloffMedium; } @@ -564,21 +612,13 @@ bool_as_string (bool yn) return (yn ? "yes" : "no"); } -bool -string_is_affirmative (const std::string& str) -{ - /* to be used only with XML data - not intended to handle user input */ - - return str == "1" || str == "y" || str == "Y" || (!g_strncasecmp(str.c_str(), "yes", str.length())); -} - const char* native_header_format_extension (HeaderFormat hf, const DataType& type) { if (type == DataType::MIDI) { return ".mid"; } - + switch (hf) { case BWF: return ".wav"; @@ -614,28 +654,28 @@ matching_unsuffixed_filename_exists_in (const string& dir, const string& path) error << string_compose (_("cannot open directory %1 (%2)"), dir, strerror (errno)) << endl; return false; } - + while ((dentry = ::readdir (dead)) != 0) { - + /* avoid '.' and '..' */ - + if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') || (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) { continue; } - + string fullpath = Glib::build_filename (dir, dentry->d_name); if (::stat (fullpath.c_str(), &statbuf)) { continue; } - + if (!S_ISREG (statbuf.st_mode)) { continue; } string bws2 = basename_nosuffix (dentry->d_name); - + if (bws2 == bws) { ret = true; break; @@ -646,6 +686,50 @@ matching_unsuffixed_filename_exists_in (const string& dir, const string& path) return ret; } +uint32_t +how_many_dsp_threads () +{ + /* CALLER MUST HOLD PROCESS LOCK */ + + int num_cpu = hardware_concurrency(); + int pu = Config->get_processor_usage (); + uint32_t num_threads = max (num_cpu - 1, 2); // default to number of cpus minus one, or 2, whichever is larger + + if (pu < 0) { + /* pu is negative: use "pu" less cores for DSP than appear to be available + */ + + if (-pu < num_cpu) { + num_threads = num_cpu + pu; + } + + } else if (pu == 0) { + + /* use all available CPUs + */ + + num_threads = num_cpu; + + } else { + /* use "pu" cores, if available + */ + + num_threads = min (num_cpu, pu); + } + + return num_threads; +} + +double gain_to_slider_position_with_max (double g, double max_gain) +{ + return gain_to_slider_position (g * 2.0/max_gain); +} + +double slider_position_to_gain_with_max (double g, double max_gain) +{ + return slider_position_to_gain (g * max_gain/2.0); +} + extern "C" { void c_stacktrace() { stacktrace (cerr); } }