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