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