X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Futils.cc;h=f9d16a47b49050ff9c661838d0896ae8e88a7cc3;hb=a406d9183adc67075a4e802fd8254c2560df9964;hp=d8642486d9e9fb9d15a3c238103dae38b4507c82;hpb=09ee5d9967328c1fabd4aca7766f9613acd8ee5e;p=ardour.git diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index d8642486d9..f9d16a47b4 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,28 +15,41 @@ 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 #ifdef HAVE_WORDEXP #include #endif -#include -#include -#include +#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 "i18n.h" @@ -44,51 +57,10 @@ 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) { +#if OLD_SCHOOL_PROHIBITIVE string::size_type pos; string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: "; string legal; @@ -102,15 +74,105 @@ legalize_for_path (string str) } return legal; +#else + string::size_type pos; + string illegal_chars = "/\\"; /* DOS, POSIX. Yes, we're going to ignore HFS */ + string legal; + + legal = str; + pos = 0; + + while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) { + legal.replace (pos, 1, "_"); + pos += 1; + } + + return legal; +#endif } -ostream& -operator<< (ostream& o, const BBT_Time& bbt) +string +bump_name_once (const std::string& name, char delimiter) { - o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks; - return o; + 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; + } +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) { @@ -137,7 +199,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; @@ -145,34 +207,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 @@ -187,44 +223,76 @@ 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; @@ -248,20 +316,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)) { @@ -270,27 +338,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); @@ -301,10 +369,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*/ @@ -316,49 +386,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 @@ -366,35 +444,213 @@ 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 +ARDOUR::string_to_auto_state (std::string str) +{ + if (str == X_("Off")) { + return Off; + } else if (str == X_("Play")) { + return Play; + } else if (str == X_("Write")) { + return Write; + } else if (str == X_("Touch")) { + return Touch; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg; + /*NOTREACHED*/ + return Touch; +} + +string +ARDOUR::auto_state_to_string (AutoState as) +{ + /* to be used only for XML serialization, no i18n done */ + + switch (as) { + case Off: + return X_("Off"); + break; + case Play: + return X_("Play"); + break; + case Write: + return X_("Write"); + break; + case Touch: + return X_("Touch"); + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg; + /*NOTREACHED*/ + return ""; +} + +AutoStyle +ARDOUR::string_to_auto_style (std::string str) +{ + if (str == X_("Absolute")) { + return Absolute; + } else if (str == X_("Trim")) { + return Trim; + } + + fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg; + /*NOTREACHED*/ + return Trim; +} + +string +ARDOUR::auto_style_to_string (AutoStyle as) +{ + /* to be used only for XML serialization, no i18n done */ + + switch (as) { + case Absolute: + return X_("Absolute"); + break; + case Trim: + 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 */ + + 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; +} + +extern "C" { + void c_stacktrace() { stacktrace (cerr); } }