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