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