X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Futils.cc;h=c1192c4fe123acd645572cddd1bd08acbd54b6b6;hb=80ec3fb37e0156cf02a25e9b353879819b66057d;hp=6cbb8e0c447fac6f6df7e8354753b4c31a8e1c68;hpb=40a6f878a945d96e51839d036992521dd5883e05;p=ardour.git diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 6cbb8e0c44..c1192c4fe1 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2003 Paul Davis + Copyright (C) 2000-2003 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,29 +15,45 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ +#ifdef WAF_BUILD +#include "libardour-config.h" +#endif + +#include + #include /* for sprintf */ +#include +#include #include #include -#include +#include #include #include #include #include #include #include -#include +#include +#include + +#include +#include #ifdef HAVE_WORDEXP #include #endif -#include -#include -#include -#include +#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 "ardour/utils.h" +#include "ardour/rc_configuration.h" #include "i18n.h" @@ -45,73 +61,106 @@ using namespace ARDOUR; using namespace std; using namespace PBD; -void -elapsed_time_to_str (char *buf, uint32_t seconds) - -{ - uint32_t days; - uint32_t hours; - uint32_t minutes; - uint32_t s; - - s = seconds; - days = s / (3600 * 24); - s -= (days * 3600 * 24); - hours = s / 3600; - s -= (hours * 3600); - minutes = s / 60; - s -= minutes * 60; - - if (days) { - snprintf (buf, sizeof (buf), "%" PRIu32 " day%s %" PRIu32 " hour%s", - days, - days > 1 ? "s" : "", - hours, - hours > 1 ? "s" : ""); - } else if (hours) { - snprintf (buf, sizeof (buf), "%" PRIu32 " hour%s %" PRIu32 " minute%s", - hours, - hours > 1 ? "s" : "", - minutes, - minutes > 1 ? "s" : ""); - } else if (minutes) { - snprintf (buf, sizeof (buf), "%" PRIu32 " minute%s", - minutes, - minutes > 1 ? "s" : ""); - } else if (s) { - snprintf (buf, sizeof (buf), "%" PRIu32 " second%s", - seconds, - seconds > 1 ? "s" : ""); - } else { - snprintf (buf, sizeof (buf), "no time"); - } -} - -string -legalize_for_path (string str) +string +legalize_for_path (const string& str) { string::size_type pos; - string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: "; + string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */ string legal; 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; + return string (legal); +} + +string +bump_name_once (const std::string& name, char delimiter) +{ + string::size_type delim; + string newname; + + if ((delim = name.find_last_of (delimiter)) == string::npos) { + newname = name; + newname += delimiter; + newname += "1"; + } else { + int isnumber = 1; + const char *last_element = name.c_str() + delim + 1; + for (size_t i = 0; i < strlen(last_element); i++) { + if (!isdigit(last_element[i])) { + isnumber = 0; + break; + } + } + + errno = 0; + int32_t version = strtol (name.c_str()+delim+1, (char **)NULL, 10); + + if (isnumber == 0 || errno != 0) { + // last_element is not a number, or is too large + newname = name; + newname += delimiter; + newname += "1"; + } else { + char buf[32]; + + snprintf (buf, sizeof(buf), "%d", version+1); + + newname = name.substr (0, delim+1); + newname += buf; + } + } + + return newname; + } -ostream& -operator<< (ostream& o, const BBT_Time& bbt) +bool +could_be_a_valid_path (const string& path) { - o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks; - return o; + 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) { @@ -138,7 +187,7 @@ cmp_nocase (const string& s, const string& s2) { string::const_iterator p = s.begin(); string::const_iterator p2 = s2.begin(); - + while (p != s.end() && p2 != s2.end()) { if (toupper(*p) != toupper(*p2)) { return (toupper(*p) < toupper(*p2)) ? -1 : 1; @@ -146,34 +195,8 @@ cmp_nocase (const string& s, const string& s2) ++p; ++p2; } - - return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1; -} - -int -tokenize_fullpath (string fullpath, string& path, string& name) -{ - string::size_type m = fullpath.find_last_of("/"); - - if (m == string::npos) { - path = fullpath; - name = fullpath; - return 1; - } - // does it look like just a directory? - if (m == fullpath.length()-1) { - return -1; - } - path = fullpath.substr(0, m+1); - - string::size_type n = fullpath.find(".ardour", m); - // no .ardour? - if (n == string::npos) { - return -1; - } - name = fullpath.substr(m+1, n - m - 1); - return 1; + return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1; } int @@ -188,59 +211,116 @@ touch_file (string path) } string -placement_as_string (Placement p) +region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one) { - switch (p) { - case PreFader: - return _("pre"); - default: /* to get g++ to realize we have all the cases covered */ - case PostFader: - return _("post"); + path = PBD::basename_nosuffix (path); + + if (strip_channels) { + + /* remove any "?R", "?L" or "?[a-z]" channel identifier */ + + string::size_type len = path.length(); + + if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') && + (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) { + + path = path.substr (0, path.length() - 2); + } } + + if (add_channel_suffix) { + + path += '%'; + + if (total > 2) { + path += (char) ('a' + this_one); + } else { + path += (char) (this_one == 0 ? 'L' : 'R'); + } + } + + return path; } -string -region_name_from_path (string path) +bool +path_is_paired (string path, string& pair_base) { string::size_type pos; - /* remove filename suffixes etc. */ - + /* remove any leading path */ + + if ((pos = path.find_last_of (G_DIR_SEPARATOR)) != string::npos) { + path = path.substr(pos+1); + } + + /* remove filename suffixes etc. */ + if ((pos = path.find_last_of ('.')) != string::npos) { path = path.substr (0, pos); } - /* remove any "?R", "?L" or "?[a-z]" channel identifier */ - string::size_type len = path.length(); - - if (len > 3 && (path[len-2] == '%' || path[len-2] == '?') && + + /* look for possible channel identifier: "?R", "%R", ".L" etc. */ + + if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') && (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) { - - path = path.substr (0, path.length() - 2); + + pair_base = path.substr (0, len-2); + return true; + } - return path; -} + 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)) { + + /* force field expansion to avoid use whitespace, since we know this is + * a path + */ + + char *oifs = getenv ("IFS"); + setenv ("IFS", "/", 1); + int result = wordexp (path.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF); + if (oifs) { + setenv ("IFS", oifs, 1); + } else { + unsetenv ("IFS"); + } + + switch (result) { case 0: break; + case WRDE_NOSPACE: + /* see docs on wordexp() */ + wordfree (&expansion); + /* fallthru */ 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; + string all; + for (unsigned int i = 0; i < expansion.we_wordc; ++i) { + if (i > 0) { + all += " | "; + } + all += expansion.we_wordv[i]; + } + error << string_compose (_("path (%1) is ambiguous: %2"), path, all) << endmsg; goto out; } @@ -249,20 +329,20 @@ path_expand (string path) wordfree (&expansion); return ret; -#else +#else return path; #endif } -#if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS) -string +#if __APPLE__ +string CFStringRefToStdString(CFStringRef stringRef) { - CFIndex size = - CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) , + CFIndex size = + CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) , kCFStringEncodingUTF8); char *buf = new char[size]; - + std::string result; if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) { @@ -271,27 +351,27 @@ CFStringRefToStdString(CFStringRef stringRef) delete [] buf; return result; } -#endif // HAVE_COREAUDIO +#endif // __APPLE__ void -compute_equal_power_fades (nframes_t nframes, float* in, float* out) +compute_equal_power_fades (framecnt_t nframes, float* in, float* out) { double step; - step = 1.0/nframes; + step = 1.0/(nframes-1); in[0] = 0.0f; - - for (nframes_t i = 1; i < nframes - 1; ++i) { + + for (framecnt_t i = 1; i < nframes - 1; ++i) { in[i] = in[i-1] + step; } - + in[nframes-1] = 1.0; const float pan_law_attenuation = -3.0f; const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); - for (unsigned long n = 0; n < nframes; ++n) { + for (framecnt_t n = 0; n < nframes; ++n) { float inVal = in[n]; float outVal = 1 - inVal; out[n] = outVal * (scale * outVal + 1.0f - scale); @@ -302,10 +382,12 @@ compute_equal_power_fades (nframes_t nframes, float* in, float* out) EditMode string_to_edit_mode (string str) { - if (str == _("Splice Edit")) { + if (str == _("Splice")) { return Splice; - } else if (str == _("Slide Edit")) { + } else if (str == _("Slide")) { return Slide; + } else if (str == _("Lock")) { + return Lock; } fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg; /*NOTREACHED*/ @@ -317,49 +399,57 @@ edit_mode_to_string (EditMode mode) { switch (mode) { case Slide: - return _("Slide Edit"); + return _("Slide"); + + case Lock: + return _("Lock"); default: case Splice: - return _("Splice Edit"); + return _("Splice"); } } -SlaveSource -string_to_slave_source (string str) +SyncSource +string_to_sync_source (string str) { - if (str == _("Internal")) { - return None; - } - - if (str == _("MTC")) { + if (str == _("MIDI Timecode") || str == _("MTC")) { return MTC; } + if (str == _("MIDI Clock")) { + return MIDIClock; + } + if (str == _("JACK")) { return JACK; } - fatal << string_compose (_("programming error: unknown slave source string \"%1\""), str) << endmsg; + fatal << string_compose (_("programming error: unknown sync source string \"%1\""), str) << endmsg; /*NOTREACHED*/ - return None; + return JACK; } +/** @param sh Return a short version of the string */ const char* -slave_source_to_string (SlaveSource src) +sync_source_to_string (SyncSource src, bool sh) { switch (src) { case JACK: return _("JACK"); case MTC: - return _("MTC"); - - default: - case None: - return _("Internal"); - + if (sh) { + return _("MTC"); + } else { + return _("MIDI Timecode"); + } + + case MIDIClock: + return _("MIDI Clock"); } + /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */ + return _("JACK"); } float @@ -367,40 +457,51 @@ meter_falloff_to_float (MeterFalloff falloff) { switch (falloff) { case MeterFalloffOff: - return 0.0f; + return METER_FALLOFF_OFF; case MeterFalloffSlowest: - return 1.0f; + return METER_FALLOFF_SLOWEST; case MeterFalloffSlow: - return 2.0f; + return METER_FALLOFF_SLOW; case MeterFalloffMedium: - return 3.0f; + return METER_FALLOFF_MEDIUM; case MeterFalloffFast: - return 4.0f; + return METER_FALLOFF_FAST; case MeterFalloffFaster: - return 5.0f; + return METER_FALLOFF_FASTER; case MeterFalloffFastest: + return METER_FALLOFF_FASTEST; default: - return 6.0f; + return METER_FALLOFF_FAST; } } -float -meter_hold_to_float (MeterHold hold) +MeterFalloff +meter_falloff_from_float (float val) { - switch (hold) { - case MeterHoldOff: - return 0.0f; - case MeterHoldShort: - return 40.0f; - case MeterHoldMedium: - return 100.0f; - case MeterHoldLong: - default: - return 200.0f; + if (val == METER_FALLOFF_OFF) { + return MeterFalloffOff; + } + else if (val <= METER_FALLOFF_SLOWEST) { + return MeterFalloffSlowest; + } + else if (val <= METER_FALLOFF_SLOW) { + return MeterFalloffSlow; + } + else if (val <= METER_FALLOFF_MEDIUM) { + return MeterFalloffMedium; + } + else if (val <= METER_FALLOFF_FAST) { + return MeterFalloffFast; + } + else if (val <= METER_FALLOFF_FASTER) { + return MeterFalloffFaster; + } + else { + return MeterFalloffFastest; } } -AutoState +AutoState ARDOUR::string_to_auto_state (std::string str) { if (str == X_("Off")) { @@ -415,9 +516,10 @@ ARDOUR::string_to_auto_state (std::string str) fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg; /*NOTREACHED*/ + return Touch; } -string +string ARDOUR::auto_state_to_string (AutoState as) { /* to be used only for XML serialization, no i18n done */ @@ -435,9 +537,13 @@ ARDOUR::auto_state_to_string (AutoState as) case Touch: return X_("Touch"); } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg; + /*NOTREACHED*/ + return ""; } -AutoStyle +AutoStyle ARDOUR::string_to_auto_style (std::string str) { if (str == X_("Absolute")) { @@ -448,9 +554,10 @@ ARDOUR::string_to_auto_style (std::string str) fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg; /*NOTREACHED*/ + return Trim; } -string +string ARDOUR::auto_style_to_string (AutoStyle as) { /* to be used only for XML serialization, no i18n done */ @@ -463,6 +570,153 @@ ARDOUR::auto_style_to_string (AutoStyle as) return X_("Trim"); break; } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg; + /*NOTREACHED*/ + return ""; +} + +std::string +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 */ + + if (str.empty ()) { + return false; + } + + /* the use of g_strncasecmp() is solely to get around issues with + * charsets posed by trying to use C++ for the same + * comparison. switching a std::string to its lower- or upper-case + * version has several issues, but handled by default + * in the way we desire when doing it in C. + */ + + 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"; + case WAVE: + return ".wav"; + case WAVE64: + return ".w64"; + case CAF: + return ".caf"; + case AIFF: + return ".aif"; + case iXML: + return ".ixml"; + case RF64: + return ".rf64"; + } + + fatal << string_compose (_("programming error: unknown native header format: %1"), hf); + /*NOTREACHED*/ + return ".wav"; +} + +bool +matching_unsuffixed_filename_exists_in (const string& dir, const string& path) +{ + string bws = basename_nosuffix (path); + struct dirent* dentry; + struct stat statbuf; + DIR* dead; + bool ret = false; + + if ((dead = ::opendir (dir.c_str())) == 0) { + 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; + } + } + + ::closedir (dead); + 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" {