X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Futils.cc;h=f23f1332b8a471333a04d1ca33e81058a37aab34;hb=4861eca97483128e5febb575b94688581abb0154;hp=1906d92b887dfe3f5c9f52e10927f43baa121300;hpb=68e943265edf04e63a8e8b8f62bab20f99d9c637;p=ardour.git diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 1906d92b88..f23f1332b8 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -17,14 +17,18 @@ */ -#define __STDC_FORMAT_MACROS 1 +#ifdef WAF_BUILD +#include "libardour-config.h" +#endif + #include #include /* for sprintf */ #include +#include +#include #include #include -#include #include #include #include @@ -32,31 +36,112 @@ #include #include #include -#include - -#ifdef HAVE_WORDEXP -#include +#ifndef COMPILER_MSVC +#include #endif +#include +#include + +#include +#include -#include -#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 "pbd/replace_all.h" + +#include "ardour/utils.h" +#include "ardour/rc_configuration.h" #include "i18n.h" using namespace ARDOUR; using namespace std; using namespace PBD; -using Glib::ustring; -ustring -legalize_for_path (ustring str) +static string +replace_chars (const string& str, const string& illegal_chars) { - ustring::size_type pos; - ustring legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: "; - ustring legal; + string::size_type pos; + 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) { + legal.replace (pos, 1, "_"); + pos += 1; + } + + 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 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; @@ -66,20 +151,22 @@ legalize_for_path (ustring str) pos += 1; } - return legal; + return string (legal); } -string bump_name_once(std::string name) +string +bump_name_once (const std::string& name, char delimiter) { - string::size_type period; + string::size_type delim; string newname; - if ((period = name.find_last_of ('.')) == string::npos) { + if ((delim = name.find_last_of (delimiter)) == string::npos) { newname = name; - newname += ".1"; + newname += delimiter; + newname += "1"; } else { int isnumber = 1; - const char *last_element = name.c_str() + period + 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; @@ -88,18 +175,19 @@ string bump_name_once(std::string name) } errno = 0; - long int version = strtol (name.c_str()+period+1, (char **)NULL, 10); + 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 += ".1"; + newname += delimiter; + newname += "1"; } else { char buf[32]; - snprintf (buf, sizeof(buf), "%ld", version+1); + snprintf (buf, sizeof(buf), "%d", version+1); - newname = name.substr (0, period+1); + newname = name.substr (0, delim+1); newname += buf; } } @@ -108,13 +196,6 @@ string bump_name_once(std::string name) } -ostream& -operator<< (ostream& o, const BBT_Time& bbt) -{ - o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks; - return o; -} - XMLNode * find_named_node (const XMLNode& node, string name) { @@ -154,7 +235,7 @@ cmp_nocase (const string& s, const string& s2) } int -touch_file (ustring path) +touch_file (string path) { int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660); if (fd >= 0) { @@ -164,8 +245,8 @@ touch_file (ustring path) return 1; } -ustring -region_name_from_path (ustring path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one) +string +region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one) { path = PBD::basename_nosuffix (path); @@ -173,7 +254,7 @@ region_name_from_path (ustring path, bool strip_channels, bool add_channel_suffi /* remove any "?R", "?L" or "?[a-z]" channel identifier */ - ustring::size_type len = path.length(); + 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])))) { @@ -197,23 +278,23 @@ region_name_from_path (ustring path, bool strip_channels, bool add_channel_suffi } bool -path_is_paired (ustring path, ustring& pair_base) +path_is_paired (string path, string& pair_base) { - ustring::size_type pos; + string::size_type pos; /* remove any leading path */ - if ((pos = path.find_last_of ('/')) != string::npos) { + if ((pos = path.find_last_of (G_DIR_SEPARATOR)) != string::npos) { path = path.substr(pos+1); } - /* remove filename suffixes etc. */ + /* remove filename suffixes etc. */ if ((pos = path.find_last_of ('.')) != string::npos) { path = path.substr (0, pos); } - ustring::size_type len = path.length(); + string::size_type len = path.length(); /* look for possible channel identifier: "?R", "%R", ".L" etc. */ @@ -228,38 +309,7 @@ path_is_paired (ustring path, ustring& pair_base) return false; } -ustring -path_expand (ustring 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 defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS) +#if __APPLE__ string CFStringRefToStdString(CFStringRef stringRef) { @@ -276,18 +326,18 @@ 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; } @@ -296,7 +346,7 @@ compute_equal_power_fades (nframes_t nframes, float* in, float* out) const float pan_law_attenuation = -3.0f; const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f); - for (nframes_t 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); @@ -307,11 +357,11 @@ 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 Edit")) { + } else if (str == _("Lock")) { return Lock; } fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg; @@ -324,25 +374,21 @@ edit_mode_to_string (EditMode mode) { switch (mode) { case Slide: - return _("Slide Edit"); + return _("Slide"); case Lock: - return _("Lock Edit"); + 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; } @@ -354,44 +400,40 @@ string_to_slave_source (string str) 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"); + if (sh) { + return _("MTC"); + } else { + return _("MIDI Timecode"); + } case MIDIClock: - return _("MIDI Clock"); - - default: - case None: - return _("Internal"); + 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"); } -/* I don't really like hard-coding these falloff rates here - * Probably should use a map of some kind that could be configured - * These rates are db/sec. -*/ - -#define METER_FALLOFF_OFF 0.0f -#define METER_FALLOFF_SLOWEST 6.6f // BBC standard -#define METER_FALLOFF_SLOW 8.6f // BBC standard -#define METER_FALLOFF_MEDIUM 20.0f -#define METER_FALLOFF_FAST 32.0f -#define METER_FALLOFF_FASTER 46.0f -#define METER_FALLOFF_FASTEST 70.0f - float meter_falloff_to_float (MeterFalloff falloff) { @@ -402,8 +444,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: @@ -427,6 +473,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; } @@ -435,28 +487,12 @@ meter_falloff_from_float (float val) } else if (val <= METER_FALLOFF_FASTER) { return MeterFalloffFaster; - } + } else { return MeterFalloffFastest; } } -float -meter_hold_to_float (MeterHold hold) -{ - switch (hold) { - case MeterHoldOff: - return 0.0f; - case MeterHoldShort: - return 40.0f; - case MeterHoldMedium: - return 100.0f; - case MeterHoldLong: - default: - return 200.0f; - } -} - AutoState ARDOUR::string_to_auto_state (std::string str) { @@ -532,6 +568,130 @@ ARDOUR::auto_style_to_string (AutoStyle as) return ""; } +std::string +bool_as_string (bool yn) +{ + return (yn ? "yes" : "no"); +} + +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" { void c_stacktrace() { stacktrace (cerr); } }