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