ardour
utils.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2003 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
23 
24 #include <stdint.h>
25 
26 #include <cstdio> /* for sprintf */
27 #include <cstring>
28 #include <climits>
29 #include <cstdlib>
30 #include <cmath>
31 #include <cctype>
32 #include <cstring>
33 #include <cerrno>
34 #include <iostream>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <fcntl.h>
39 #ifndef COMPILER_MSVC
40 #include <dirent.h>
41 #endif
42 #include <errno.h>
43 #include <regex.h>
44 
45 #include <glibmm/miscutils.h>
46 #include <glibmm/fileutils.h>
47 
48 #include "pbd/cpus.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"
55 
56 #include "ardour/utils.h"
58 
59 #include "i18n.h"
60 
61 using namespace ARDOUR;
62 using namespace std;
63 using namespace PBD;
64 
65 static string
66 replace_chars (const string& str, const string& illegal_chars)
67 {
68  string::size_type pos;
69  Glib::ustring legal;
70 
71  /* this is the one place in Ardour where we need to iterate across
72  * potential multibyte characters, and thus we need Glib::ustring
73  */
74 
75  legal = str;
76  pos = 0;
77 
78  while ((pos = legal.find_first_of (illegal_chars, pos)) != string::npos) {
79  legal.replace (pos, 1, "_");
80  pos += 1;
81  }
82 
83  return string (legal);
84 }
95 string
96 ARDOUR::legalize_for_path (const string& str)
97 {
98  return replace_chars (str, "/\\");
99 }
100 
110 string
111 ARDOUR::legalize_for_universal_path (const string& str)
112 {
113  return replace_chars (str, "<>:\"/\\|?*");
114 }
115 
121 string
122 ARDOUR::legalize_for_uri (const string& str)
123 {
124  return replace_chars (str, "<>:\"/\\|?* #");
125 }
126 
135 string
136 ARDOUR::legalize_for_path_2X (const string& str)
137 {
138  string::size_type pos;
139  string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
140  Glib::ustring legal;
141 
142  /* this is the one place in Ardour where we need to iterate across
143  * potential multibyte characters, and thus we need Glib::ustring
144  */
145 
146  legal = str;
147  pos = 0;
148 
149  while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
150  legal.replace (pos, 1, "_");
151  pos += 1;
152  }
153 
154  return string (legal);
155 }
156 
157 string
158 ARDOUR::bump_name_once (const std::string& name, char delimiter)
159 {
160  string::size_type delim;
161  string newname;
162 
163  if ((delim = name.find_last_of (delimiter)) == string::npos) {
164  newname = name;
165  newname += delimiter;
166  newname += "1";
167  } else {
168  int isnumber = 1;
169  const char *last_element = name.c_str() + delim + 1;
170  for (size_t i = 0; i < strlen(last_element); i++) {
171  if (!isdigit(last_element[i])) {
172  isnumber = 0;
173  break;
174  }
175  }
176 
177  errno = 0;
178  int32_t version = strtol (name.c_str()+delim+1, (char **)NULL, 10);
179 
180  if (isnumber == 0 || errno != 0) {
181  // last_element is not a number, or is too large
182  newname = name;
183  newname += delimiter;
184  newname += "1";
185  } else {
186  char buf[32];
187 
188  snprintf (buf, sizeof(buf), "%d", version+1);
189 
190  newname = name.substr (0, delim+1);
191  newname += buf;
192  }
193  }
194 
195  return newname;
196 
197 }
198 
199 string
200 ARDOUR::bump_name_number (const std::string& name)
201 {
202  size_t pos = name.length();
203  size_t num = 0;
204  bool have_number = false;
205  while (pos > 0 && isdigit(name.at(--pos))) {
206  have_number = true;
207  num = pos;
208  }
209 
210  string newname;
211  if (have_number) {
212  int32_t seq = strtol (name.c_str() + num, (char **)NULL, 10);
213  char buf[32];
214  snprintf (buf, sizeof(buf), "%d", seq + 1);
215  newname = name.substr (0, num);
216  newname += buf;
217  } else {
218  newname = name;
219  newname += "1";
220  }
221 
222  return newname;
223 }
224 
225 XMLNode *
226 ARDOUR::find_named_node (const XMLNode& node, string name)
227 {
228  XMLNodeList nlist;
229  XMLNodeConstIterator niter;
230  XMLNode* child;
231 
232  nlist = node.children();
233 
234  for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
235 
236  child = *niter;
237 
238  if (child->name() == name) {
239  return child;
240  }
241  }
242 
243  return 0;
244 }
245 
246 int
247 ARDOUR::cmp_nocase (const string& s, const string& s2)
248 {
249  string::const_iterator p = s.begin();
250  string::const_iterator p2 = s2.begin();
251 
252  while (p != s.end() && p2 != s2.end()) {
253  if (toupper(*p) != toupper(*p2)) {
254  return (toupper(*p) < toupper(*p2)) ? -1 : 1;
255  }
256  ++p;
257  ++p2;
258  }
259 
260  return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
261 }
262 
263 int
264 ARDOUR::cmp_nocase_utf8 (const string& s1, const string& s2)
265 {
266  const char *cstr1 = s1.c_str();
267  const char *cstr2 = s2.c_str();
268  gchar *cstr1folded = NULL;
269  gchar *cstr2folded = NULL;
270  int retval;
271 
272  if (!g_utf8_validate (cstr1, -1, NULL) ||
273  !g_utf8_validate (cstr2, -1, NULL)) {
274  // fall back to comparing ASCII
275  return g_ascii_strcasecmp (cstr1, cstr2);
276  }
277 
278  cstr1folded = g_utf8_casefold (cstr1, -1);
279  cstr2folded = g_utf8_casefold (cstr2, -1);
280 
281  if (cstr1folded && cstr2folded) {
282  retval = strcmp (cstr1folded, cstr2folded);
283  } else {
284  // this shouldn't happen, make the best of it
285  retval = g_ascii_strcasecmp (cstr1, cstr2);
286  }
287 
288  if (cstr1folded) {
289  g_free (cstr1folded);
290  }
291 
292  if (cstr2folded) {
293  g_free (cstr2folded);
294  }
295 
296  return retval;
297 }
298 
299 int
300 ARDOUR::touch_file (string path)
301 {
302  int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
303  if (fd >= 0) {
304  close (fd);
305  return 0;
306  }
307  return 1;
308 }
309 
310 string
311 ARDOUR::region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
312 {
313  path = PBD::basename_nosuffix (path);
314 
315  if (strip_channels) {
316 
317  /* remove any "?R", "?L" or "?[a-z]" channel identifier */
318 
319  string::size_type len = path.length();
320 
321  if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
322  (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
323 
324  path = path.substr (0, path.length() - 2);
325  }
326  }
327 
328  if (add_channel_suffix) {
329 
330  path += '%';
331 
332  if (total > 2) {
333  path += (char) ('a' + this_one);
334  } else {
335  path += (char) (this_one == 0 ? 'L' : 'R');
336  }
337  }
338 
339  return path;
340 }
341 
342 bool
343 ARDOUR::path_is_paired (string path, string& pair_base)
344 {
345  string::size_type pos;
346 
347  /* remove any leading path */
348 
349  if ((pos = path.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
350  path = path.substr(pos+1);
351  }
352 
353  /* remove filename suffixes etc. */
354 
355  if ((pos = path.find_last_of ('.')) != string::npos) {
356  path = path.substr (0, pos);
357  }
358 
359  string::size_type len = path.length();
360 
361  /* look for possible channel identifier: "?R", "%R", ".L" etc. */
362 
363  if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
364  (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
365 
366  pair_base = path.substr (0, len-2);
367  return true;
368 
369  }
370 
371  return false;
372 }
373 
374 #if __APPLE__
375 string
376 ARDOUR::CFStringRefToStdString(CFStringRef stringRef)
377 {
378  CFIndex size =
379  CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
380  kCFStringEncodingUTF8);
381  char *buf = new char[size];
382 
383  std::string result;
384 
385  if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) {
386  result = buf;
387  }
388  delete [] buf;
389  return result;
390 }
391 #endif // __APPLE__
392 
393 void
394 ARDOUR::compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
395 {
396  double step;
397 
398  step = 1.0/(nframes-1);
399 
400  in[0] = 0.0f;
401 
402  for (framecnt_t i = 1; i < nframes - 1; ++i) {
403  in[i] = in[i-1] + step;
404  }
405 
406  in[nframes-1] = 1.0;
407 
408  const float pan_law_attenuation = -3.0f;
409  const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
410 
411  for (framecnt_t n = 0; n < nframes; ++n) {
412  float inVal = in[n];
413  float outVal = 1 - inVal;
414  out[n] = outVal * (scale * outVal + 1.0f - scale);
415  in[n] = inVal * (scale * inVal + 1.0f - scale);
416  }
417 }
418 
419 EditMode
420 ARDOUR::string_to_edit_mode (string str)
421 {
422  if (str == _("Splice")) {
423  return Splice;
424  } else if (str == _("Slide")) {
425  return Slide;
426  } else if (str == _("Ripple")) {
427  return Ripple;
428  } else if (str == _("Lock")) {
429  return Lock;
430  }
431  fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg;
432  abort(); /*NOTREACHED*/
433  return Slide;
434 }
435 
436 const char*
438 {
439  switch (mode) {
440  case Slide:
441  return _("Slide");
442 
443  case Lock:
444  return _("Lock");
445 
446  case Ripple:
447  return _("Ripple");
448 
449  default:
450  case Splice:
451  return _("Splice");
452  }
453 }
454 
457 {
458  if (str == _("MIDI Timecode") || str == _("MTC")) {
459  return MTC;
460  }
461 
462  if (str == _("MIDI Clock")) {
463  return MIDIClock;
464  }
465 
466  if (str == _("JACK")) {
467  return Engine;
468  }
469 
470  fatal << string_compose (_("programming error: unknown sync source string \"%1\""), str) << endmsg;
471  abort(); /*NOTREACHED*/
472  return Engine;
473 }
474 
476 const char*
478 {
479  switch (src) {
480  case Engine:
481  /* no other backends offer sync for now ... deal with this if we
482  * ever have to.
483  */
484  return _("JACK");
485 
486  case MTC:
487  if (sh) {
488  return _("MTC");
489  } else {
490  return _("MIDI Timecode");
491  }
492 
493  case MIDIClock:
494  if (sh) {
495  return _("M-Clock");
496  } else {
497  return _("MIDI Clock");
498  }
499 
500  case LTC:
501  return _("LTC");
502  }
503  /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
504  return _("JACK");
505 }
506 
507 float
509 {
510  switch (falloff) {
511  case MeterFalloffOff:
512  return METER_FALLOFF_OFF;
513  case MeterFalloffSlowest:
514  return METER_FALLOFF_SLOWEST;
515  case MeterFalloffSlow:
516  return METER_FALLOFF_SLOW;
517  case MeterFalloffSlowish:
518  return METER_FALLOFF_SLOWISH;
519  case MeterFalloffMedium:
520  return METER_FALLOFF_MEDIUM;
522  return METER_FALLOFF_MODERATE;
523  case MeterFalloffFast:
524  return METER_FALLOFF_FAST;
525  case MeterFalloffFaster:
526  return METER_FALLOFF_FASTER;
527  case MeterFalloffFastest:
528  return METER_FALLOFF_FASTEST;
529  default:
530  return METER_FALLOFF_FAST;
531  }
532 }
533 
536 {
537  if (val == METER_FALLOFF_OFF) {
538  return MeterFalloffOff;
539  }
540  else if (val <= METER_FALLOFF_SLOWEST) {
541  return MeterFalloffSlowest;
542  }
543  else if (val <= METER_FALLOFF_SLOW) {
544  return MeterFalloffSlow;
545  }
546  else if (val <= METER_FALLOFF_SLOWISH) {
547  return MeterFalloffSlowish;
548  }
549  else if (val <= METER_FALLOFF_MODERATE) {
550  return MeterFalloffModerate;
551  }
552  else if (val <= METER_FALLOFF_MEDIUM) {
553  return MeterFalloffMedium;
554  }
555  else if (val <= METER_FALLOFF_FAST) {
556  return MeterFalloffFast;
557  }
558  else if (val <= METER_FALLOFF_FASTER) {
559  return MeterFalloffFaster;
560  }
561  else {
562  return MeterFalloffFastest;
563  }
564 }
565 
566 AutoState
568 {
569  if (str == X_("Off")) {
570  return Off;
571  } else if (str == X_("Play")) {
572  return Play;
573  } else if (str == X_("Write")) {
574  return Write;
575  } else if (str == X_("Touch")) {
576  return Touch;
577  }
578 
579  fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg;
580  abort(); /*NOTREACHED*/
581  return Touch;
582 }
583 
584 string
586 {
587  /* to be used only for XML serialization, no i18n done */
588 
589  switch (as) {
590  case Off:
591  return X_("Off");
592  break;
593  case Play:
594  return X_("Play");
595  break;
596  case Write:
597  return X_("Write");
598  break;
599  case Touch:
600  return X_("Touch");
601  }
602 
603  fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg;
604  abort(); /*NOTREACHED*/
605  return "";
606 }
607 
608 AutoStyle
610 {
611  if (str == X_("Absolute")) {
612  return Absolute;
613  } else if (str == X_("Trim")) {
614  return Trim;
615  }
616 
617  fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg;
618  abort(); /*NOTREACHED*/
619  return Trim;
620 }
621 
622 string
624 {
625  /* to be used only for XML serialization, no i18n done */
626 
627  switch (as) {
628  case Absolute:
629  return X_("Absolute");
630  break;
631  case Trim:
632  return X_("Trim");
633  break;
634  }
635 
636  fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg;
637  abort(); /*NOTREACHED*/
638  return "";
639 }
640 
641 std::string
642 bool_as_string (bool yn)
643 {
644  return (yn ? "yes" : "no");
645 }
646 
647 const char*
649 {
650  if (type == DataType::MIDI) {
651  return ".mid";
652  }
653 
654  switch (hf) {
655  case BWF:
656  return ".wav";
657  case WAVE:
658  return ".wav";
659  case WAVE64:
660  return ".w64";
661  case CAF:
662  return ".caf";
663  case AIFF:
664  return ".aif";
665  case iXML:
666  return ".ixml";
667  case RF64:
668  return ".rf64";
669  }
670 
671  fatal << string_compose (_("programming error: unknown native header format: %1"), hf);
672  abort(); /*NOTREACHED*/
673  return ".wav";
674 }
675 
676 bool
677 ARDOUR::matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
678 {
679  string bws = basename_nosuffix (path);
680  struct dirent* dentry;
681  struct stat statbuf;
682  DIR* dead;
683  bool ret = false;
684 
685  if ((dead = ::opendir (dir.c_str())) == 0) {
686  error << string_compose (_("cannot open directory %1 (%2)"), dir, strerror (errno)) << endl;
687  return false;
688  }
689 
690  while ((dentry = ::readdir (dead)) != 0) {
691 
692  /* avoid '.' and '..' */
693 
694  if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
695  (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
696  continue;
697  }
698 
699  string fullpath = Glib::build_filename (dir, dentry->d_name);
700 
701  if (::stat (fullpath.c_str(), &statbuf)) {
702  continue;
703  }
704 
705  if (!S_ISREG (statbuf.st_mode)) {
706  continue;
707  }
708 
709  string bws2 = basename_nosuffix (dentry->d_name);
710 
711  if (bws2 == bws) {
712  ret = true;
713  break;
714  }
715  }
716 
717  ::closedir (dead);
718  return ret;
719 }
720 
721 uint32_t
723 {
724  /* CALLER MUST HOLD PROCESS LOCK */
725 
726  int num_cpu = hardware_concurrency();
727  int pu = Config->get_processor_usage ();
728  uint32_t num_threads = max (num_cpu - 1, 2); // default to number of cpus minus one, or 2, whichever is larger
729 
730  if (pu < 0) {
731  /* pu is negative: use "pu" less cores for DSP than appear to be available
732  */
733 
734  if (-pu < num_cpu) {
735  num_threads = num_cpu + pu;
736  }
737 
738  } else if (pu == 0) {
739 
740  /* use all available CPUs
741  */
742 
743  num_threads = num_cpu;
744 
745  } else {
746  /* use "pu" cores, if available
747  */
748 
749  num_threads = min (num_cpu, pu);
750  }
751 
752  return num_threads;
753 }
754 
755 double
756 ARDOUR::gain_to_slider_position_with_max (double g, double max_gain)
757 {
758  return gain_to_slider_position (g * 2.0/max_gain);
759 }
760 
761 double
762 ARDOUR::slider_position_to_gain_with_max (double g, double max_gain)
763 {
764  return slider_position_to_gain (g * max_gain/2.0);
765 }
766 
767 extern "C" {
768  void c_stacktrace() { stacktrace (cerr); }
769 }
LIBARDOUR_API ARDOUR::MeterFalloff meter_falloff_from_float(float)
Definition: utils.cc:535
HeaderFormat
Definition: types.h:475
#define METER_FALLOFF_FAST
Definition: utils.h:163
LIBPBD_API Transmitter fatal
static double gain_to_slider_position(ARDOUR::gain_t g)
Definition: utils.h:83
LIBARDOUR_API bool path_is_paired(std::string path, std::string &pair_base)
SyncSource
Definition: types.h:498
LIBARDOUR_API void compute_equal_power_fades(ARDOUR::framecnt_t nframes, float *in, float *out)
Definition: utils.cc:394
LIBPBD_API void stacktrace(std::ostream &out, int levels=0)
Definition: stacktrace.cc:115
LIBARDOUR_API std::string legalize_for_uri(const std::string &str)
LIBARDOUR_API std::string legalize_for_path(const std::string &str)
LIBARDOUR_API double slider_position_to_gain_with_max(double g, double max_gain=2.0)
Definition: utils.cc:762
const std::string & name() const
Definition: xml++.h:104
LIBARDOUR_API const char * sync_source_to_string(ARDOUR::SyncSource src, bool sh=false)
Definition: utils.cc:477
tuple f
Definition: signals.py:35
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
AutoStyle string_to_auto_style(std::string)
Definition: utils.cc:609
const XMLNodeList & children(const std::string &str=std::string()) const
Definition: xml++.cc:329
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
LIBARDOUR_API uint32_t how_many_dsp_threads()
Definition: utils.cc:722
AutoStyle
Definition: types.h:155
LIBARDOUR_API int touch_file(std::string path)
void c_stacktrace()
Definition: utils.cc:768
std::list< XMLNode * > XMLNodeList
Definition: xml++.h:44
#define METER_FALLOFF_MODERATE
Definition: utils.h:161
LIBARDOUR_API bool matching_unsuffixed_filename_exists_in(const std::string &dir, const std::string &name)
#define _(Text)
Definition: i18n.h:11
LIBARDOUR_API ARDOUR::EditMode string_to_edit_mode(std::string)
static string replace_chars(const string &str, const string &illegal_chars)
Definition: utils.cc:66
#define X_(Text)
Definition: i18n.h:13
int64_t framecnt_t
Definition: types.h:76
LIBARDOUR_API std::string legalize_for_path_2X(const std::string &str)
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
std::string auto_state_to_string(AutoState)
Definition: utils.cc:585
#define METER_FALLOFF_OFF
Definition: utils.h:157
Definition: amp.h:29
#define METER_FALLOFF_FASTEST
Definition: utils.h:165
LIBARDOUR_API ARDOUR::SyncSource string_to_sync_source(std::string str)
LIBARDOUR_API int cmp_nocase_utf8(const std::string &s1, const std::string &s2)
EditMode
Definition: types.h:351
LIBARDOUR_API XMLNode * find_named_node(const XMLNode &node, std::string name)
uint32_t hardware_concurrency()
Definition: cpus.cc:42
LIBARDOUR_API int cmp_nocase(const std::string &s, const std::string &s2)
#define METER_FALLOFF_SLOW
Definition: utils.h:159
LIBARDOUR_API std::string bump_name_number(const std::string &s)
Definition: utils.cc:200
LIBARDOUR_API std::string bump_name_once(const std::string &s, char delimiter)
Definition: utils.cc:158
#define METER_FALLOFF_SLOWISH
Definition: utils.h:160
LIBPBD_API Glib::ustring basename_nosuffix(Glib::ustring)
#define METER_FALLOFF_FASTER
Definition: utils.h:164
LIBARDOUR_API double gain_to_slider_position_with_max(double g, double max_gain=2.0)
Definition: utils.cc:756
#define METER_FALLOFF_SLOWEST
Definition: utils.h:158
const char * name
LIBARDOUR_API std::string legalize_for_universal_path(const std::string &str)
Definition: xml++.h:95
Definition: debug.h:30
LIBARDOUR_API const char * edit_mode_to_string(ARDOUR::EditMode)
Definition: utils.cc:437
std::string auto_style_to_string(AutoStyle)
Definition: utils.cc:623
LIBARDOUR_API std::string bool_as_string(bool)
Definition: utils.cc:642
LIBARDOUR_API float meter_falloff_to_float(ARDOUR::MeterFalloff)
Definition: utils.cc:508
AutoState string_to_auto_state(std::string)
Definition: utils.cc:567
XMLNodeList::const_iterator XMLNodeConstIterator
Definition: xml++.h:49
#define METER_FALLOFF_MEDIUM
Definition: utils.h:162
LIBARDOUR_API std::string region_name_from_path(std::string path, bool strip_channels, bool add_channel_suffix=false, uint32_t total=0, uint32_t this_one=0)
MeterFalloff
Definition: types.h:332
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
static ARDOUR::gain_t slider_position_to_gain(double pos)
Definition: utils.h:106
LIBARDOUR_API const char * native_header_format_extension(ARDOUR::HeaderFormat, const ARDOUR::DataType &type)
Definition: utils.cc:648
AutoState
Definition: types.h:145