* added max peak hold state to IO so that the true held peaks can be displayed
[ardour.git] / libs / ardour / utils.cc
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     $Id$
19 */
20
21 #include <cstdio> /* for sprintf */
22 #include <cmath>
23 #include <cctype>
24 #include <string>
25 #include <cerrno>
26 #include <iostream>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32
33 #ifdef HAVE_WORDEXP
34 #include <wordexp.h>
35 #endif
36
37 #include <pbd/error.h>
38 #include <pbd/stacktrace.h>
39 #include <pbd/xml++.h>
40 #include <pbd/basename.h>
41 #include <ardour/utils.h>
42
43 #include "i18n.h"
44
45 using namespace ARDOUR;
46 using namespace std;
47 using namespace PBD;
48 using Glib::ustring;
49
50 void
51 elapsed_time_to_str (char *buf, uint32_t seconds)
52
53 {
54         uint32_t days;
55         uint32_t hours;
56         uint32_t minutes;
57         uint32_t s;
58
59         s = seconds;
60         days = s / (3600 * 24);
61         s -= (days * 3600 * 24);
62         hours = s / 3600;
63         s -= (hours * 3600);
64         minutes = s / 60;
65         s -= minutes * 60;
66         
67         if (days) {
68                 snprintf (buf, sizeof (buf), "%" PRIu32 " day%s %" PRIu32 " hour%s", 
69                          days, 
70                          days > 1 ? "s" : "",
71                          hours,
72                          hours > 1 ? "s" : "");
73         } else if (hours) {
74                 snprintf (buf, sizeof (buf), "%" PRIu32 " hour%s %" PRIu32 " minute%s", 
75                          hours, 
76                          hours > 1 ? "s" : "",
77                          minutes,
78                          minutes > 1 ? "s" : "");
79         } else if (minutes) {
80                 snprintf (buf, sizeof (buf), "%" PRIu32 " minute%s", 
81                          minutes,
82                          minutes > 1 ? "s" : "");
83         } else if (s) {
84                 snprintf (buf, sizeof (buf), "%" PRIu32 " second%s", 
85                          seconds,
86                          seconds > 1 ? "s" : "");
87         } else {
88                 snprintf (buf, sizeof (buf), "no time");
89         }
90 }
91
92 ustring 
93 legalize_for_path (ustring str)
94 {
95         ustring::size_type pos;
96         ustring legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
97         ustring legal;
98
99         legal = str;
100         pos = 0;
101
102         while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
103                 legal.replace (pos, 1, "_");
104                 pos += 1;
105         }
106
107         return legal;
108 }
109 #if 0
110 string 
111 legalize_for_path (string str)
112 {
113         string::size_type pos;
114         string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
115         string legal;
116
117         legal = str;
118         pos = 0;
119
120         while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
121                 legal.replace (pos, 1, "_");
122                 pos += 1;
123         }
124
125         return legal;
126 }
127 #endif
128
129 ostream&
130 operator<< (ostream& o, const BBT_Time& bbt)
131 {
132         o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
133         return o;
134 }
135
136 XMLNode *
137 find_named_node (const XMLNode& node, string name)
138 {
139         XMLNodeList nlist;
140         XMLNodeConstIterator niter;
141         XMLNode* child;
142
143         nlist = node.children();
144
145         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
146
147                 child = *niter;
148
149                 if (child->name() == name) {
150                         return child;
151                 }
152         }
153
154         return 0;
155 }
156
157 int
158 cmp_nocase (const string& s, const string& s2)
159 {
160         string::const_iterator p = s.begin();
161         string::const_iterator p2 = s2.begin();
162         
163         while (p != s.end() && p2 != s2.end()) {
164                 if (toupper(*p) != toupper(*p2)) {
165                         return (toupper(*p) < toupper(*p2)) ? -1 : 1;
166                 }
167                 ++p;
168                 ++p2;
169         }
170         
171         return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
172 }
173
174 int
175 tokenize_fullpath (string fullpath, string& path, string& name)
176 {
177         string::size_type m = fullpath.find_last_of("/");
178         
179         if (m == string::npos) {
180                 path = fullpath;
181                 name = fullpath;
182                 return 1;
183         }
184
185         // does it look like just a directory?
186         if (m == fullpath.length()-1) {
187                 return -1;
188         }
189         path = fullpath.substr(0, m+1);
190         
191         string::size_type n = fullpath.find(".ardour", m);
192         // no .ardour?
193         if (n == string::npos) {
194                 return -1;
195         }
196         name = fullpath.substr(m+1, n - m - 1);
197         return 1;
198 }
199
200 int
201 touch_file (ustring path)
202 {
203         int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
204         if (fd >= 0) {
205                 close (fd);
206                 return 0;
207         }
208         return 1;
209 }
210
211 ustring
212 region_name_from_path (ustring path, bool strip_channels)
213 {
214         path = PBD::basename_nosuffix (path);
215
216         if (strip_channels) {
217
218                 /* remove any "?R", "?L" or "?[a-z]" channel identifier */
219                 
220                 ustring::size_type len = path.length();
221                 
222                 if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') && 
223                     (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
224                         
225                         path = path.substr (0, path.length() - 2);
226                 }
227         }
228
229         return path;
230 }       
231
232 bool
233 path_is_paired (ustring path, ustring& pair_base)
234 {
235         ustring::size_type pos;
236
237         /* remove filename suffixes etc. */
238         
239         if ((pos = path.find_last_of ('.')) != string::npos) {
240                 path = path.substr (0, pos);
241         }
242
243         ustring::size_type len = path.length();
244
245         /* look for possible channel identifier: "?R", "%R", ".L" etc. */
246
247         if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') && 
248             (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
249                 
250                 pair_base = path.substr (0, len-2);
251                 return true;
252
253         } 
254
255         return false;
256 }
257
258 ustring
259 path_expand (ustring path)
260 {
261 #ifdef HAVE_WORDEXP
262         /* Handle tilde and environment variable expansion in session path */
263         string ret = path;
264
265         wordexp_t expansion;
266         switch (wordexp (path.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF)) {
267         case 0:
268                 break;
269         default:
270                 error << string_compose (_("illegal or badly-formed string used for path (%1)"), path) << endmsg;
271                 goto out;
272         }
273
274         if (expansion.we_wordc > 1) {
275                 error << string_compose (_("path (%1) is ambiguous"), path) << endmsg;
276                 goto out;
277         }
278
279         ret = expansion.we_wordv[0];
280   out:
281         wordfree (&expansion);
282         return ret;
283
284 #else 
285         return path;
286 #endif
287 }
288
289 #if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS)
290 string 
291 CFStringRefToStdString(CFStringRef stringRef)
292 {
293         CFIndex size = 
294                 CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) , 
295                 kCFStringEncodingUTF8);
296             char *buf = new char[size];
297         
298         std::string result;
299
300         if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) {
301             result = buf;
302         }
303         delete [] buf;
304         return result;
305 }
306 #endif // HAVE_COREAUDIO
307
308 void
309 compute_equal_power_fades (nframes_t nframes, float* in, float* out)
310 {
311         double step;
312
313         step = 1.0/nframes;
314
315         in[0] = 0.0f;
316         
317         for (nframes_t i = 1; i < nframes - 1; ++i) {
318                 in[i] = in[i-1] + step;
319         }
320         
321         in[nframes-1] = 1.0;
322
323         const float pan_law_attenuation = -3.0f;
324         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
325
326         for (nframes_t n = 0; n < nframes; ++n) {
327                 float inVal = in[n];
328                 float outVal = 1 - inVal;
329                 out[n] = outVal * (scale * outVal + 1.0f - scale);
330                 in[n] = inVal * (scale * inVal + 1.0f - scale);
331         }
332 }
333
334 EditMode
335 string_to_edit_mode (string str)
336 {
337         if (str == _("Splice Edit")) {
338                 return Splice;
339         } else if (str == _("Slide Edit")) {
340                 return Slide;
341         }
342         fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg;
343         /*NOTREACHED*/
344         return Slide;
345 }
346
347 const char*
348 edit_mode_to_string (EditMode mode)
349 {
350         switch (mode) {
351         case Slide:
352                 return _("Slide Edit");
353
354         default:
355         case Splice:
356                 return _("Splice Edit");
357         }
358 }
359
360 SlaveSource
361 string_to_slave_source (string str)
362 {
363         if (str == _("Internal")) {
364                 return None;
365         }
366         
367         if (str == _("MTC")) {
368                 return MTC;
369         }
370
371         if (str == _("JACK")) {
372                 return JACK;
373         }
374
375         fatal << string_compose (_("programming error: unknown slave source string \"%1\""), str) << endmsg;
376         /*NOTREACHED*/
377         return None;
378 }
379
380 const char*
381 slave_source_to_string (SlaveSource src)
382 {
383         switch (src) {
384         case JACK:
385                 return _("JACK");
386
387         case MTC:
388                 return _("MTC");
389                 
390         default:
391         case None:
392                 return _("Internal");
393                 
394         }
395 }
396
397 /* I don't really like hard-coding these falloff rates here
398  * Probably should use a map of some kind that could be configured
399  * These rates are db/sec.
400 */
401
402 #define METER_FALLOFF_OFF     0.0f
403 #define METER_FALLOFF_SLOWEST 6.6f // BBC standard
404 #define METER_FALLOFF_SLOW    8.6f // BBC standard
405 #define METER_FALLOFF_MEDIUM  20.0f
406 #define METER_FALLOFF_FAST    32.0f
407 #define METER_FALLOFF_FASTER  46.0f
408 #define METER_FALLOFF_FASTEST 70.0f
409
410 float
411 meter_falloff_to_float (MeterFalloff falloff)
412 {
413         switch (falloff) {
414         case MeterFalloffOff:
415                 return METER_FALLOFF_OFF;
416         case MeterFalloffSlowest:
417                 return METER_FALLOFF_SLOWEST;
418         case MeterFalloffSlow:
419                 return METER_FALLOFF_SLOW;
420         case MeterFalloffMedium:
421                 return METER_FALLOFF_MEDIUM;
422         case MeterFalloffFast:
423                 return METER_FALLOFF_FAST;
424         case MeterFalloffFaster:
425                 return METER_FALLOFF_FASTER;
426         case MeterFalloffFastest:
427                 return METER_FALLOFF_FASTEST;
428         default:
429                 return METER_FALLOFF_FAST;
430         }
431 }
432
433 MeterFalloff
434 meter_falloff_from_float (float val)
435 {
436         if (val == METER_FALLOFF_OFF) {
437                 return MeterFalloffOff;
438         }
439         else if (val <= METER_FALLOFF_SLOWEST) {
440                 return MeterFalloffSlowest;
441         }
442         else if (val <= METER_FALLOFF_SLOW) {
443                 return MeterFalloffSlow;
444         }
445         else if (val <= METER_FALLOFF_MEDIUM) {
446                 return MeterFalloffMedium;
447         }
448         else if (val <= METER_FALLOFF_FAST) {
449                 return MeterFalloffFast;
450         }
451         else if (val <= METER_FALLOFF_FASTER) {
452                 return MeterFalloffFaster;
453         }
454         else {
455                 return MeterFalloffFastest;
456         }
457 }
458
459 float
460 meter_hold_to_float (MeterHold hold)
461 {
462         switch (hold) {
463         case MeterHoldOff:
464                 return 0.0f;
465         case MeterHoldShort:
466                 return 40.0f;
467         case MeterHoldMedium:
468                 return 100.0f;
469         case MeterHoldLong:
470         default:
471                 return 200.0f;
472         }
473 }
474
475 AutoState 
476 ARDOUR::string_to_auto_state (std::string str)
477 {
478         if (str == X_("Off")) {
479                 return Off;
480         } else if (str == X_("Play")) {
481                 return Play;
482         } else if (str == X_("Write")) {
483                 return Write;
484         } else if (str == X_("Touch")) {
485                 return Touch;
486         }
487
488         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg;
489         /*NOTREACHED*/
490         return Touch;
491 }
492
493 string 
494 ARDOUR::auto_state_to_string (AutoState as)
495 {
496         /* to be used only for XML serialization, no i18n done */
497
498         switch (as) {
499         case Off:
500                 return X_("Off");
501                 break;
502         case Play:
503                 return X_("Play");
504                 break;
505         case Write:
506                 return X_("Write");
507                 break;
508         case Touch:
509                 return X_("Touch");
510         }
511
512         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg;
513         /*NOTREACHED*/
514         return "";
515 }
516
517 AutoStyle 
518 ARDOUR::string_to_auto_style (std::string str)
519 {
520         if (str == X_("Absolute")) {
521                 return Absolute;
522         } else if (str == X_("Trim")) {
523                 return Trim;
524         }
525
526         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg;
527         /*NOTREACHED*/
528         return Trim;
529 }
530
531 string 
532 ARDOUR::auto_style_to_string (AutoStyle as)
533 {
534         /* to be used only for XML serialization, no i18n done */
535
536         switch (as) {
537         case Absolute:
538                 return X_("Absolute");
539                 break;
540         case Trim:
541                 return X_("Trim");
542                 break;
543         }
544
545         fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg;
546         /*NOTREACHED*/
547         return "";
548 }
549
550 extern "C" {
551         void c_stacktrace() { stacktrace (cerr); }
552 }