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