6f8586648d6063548baae92e337151108050d51d
[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 #ifdef WAF_BUILD
21 #include "libardour-config.h"
22 #endif
23
24 #define __STDC_FORMAT_MACROS 1
25 #include <stdint.h>
26
27 #include <cstdio> /* for sprintf */
28 #include <cstring>
29 #include <cmath>
30 #include <cctype>
31 #include <cstring>
32 #include <cerrno>
33 #include <iostream>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/time.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39 #include <errno.h>
40
41 #include <glibmm/miscutils.h>
42
43 #ifdef HAVE_WORDEXP
44 #include <wordexp.h>
45 #endif
46
47 #include "pbd/error.h"
48 #include "pbd/stacktrace.h"
49 #include "pbd/xml++.h"
50 #include "pbd/basename.h"
51 #include "ardour/utils.h"
52
53 #include "i18n.h"
54
55 using namespace ARDOUR;
56 using namespace std;
57 using namespace PBD;
58 using Glib::ustring;
59
60 ustring
61 legalize_for_path (ustring str)
62 {
63         ustring::size_type pos;
64         ustring legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
65         ustring legal;
66
67         legal = str;
68         pos = 0;
69
70         while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
71                 legal.replace (pos, 1, "_");
72                 pos += 1;
73         }
74
75         return legal;
76 }
77
78 string 
79 bump_name_once (const std::string& name, char delimiter)
80 {
81         string::size_type delim;
82         string newname;
83
84         if ((delim = name.find_last_of (delimiter)) == string::npos) {
85                 newname  = name;
86                 newname += delimiter;
87                 newname += "1";
88         } else {
89                 int isnumber = 1;
90                 const char *last_element = name.c_str() + delim + 1;
91                 for (size_t i = 0; i < strlen(last_element); i++) {
92                         if (!isdigit(last_element[i])) {
93                                 isnumber = 0;
94                                 break;
95                         }
96                 }
97
98                 errno = 0;
99                 int32_t version = strtol (name.c_str()+delim+1, (char **)NULL, 10);
100
101                 if (isnumber == 0 || errno != 0) {
102                         // last_element is not a number, or is too large
103                         newname  = name;
104                         newname  += delimiter;
105                         newname += "1";
106                 } else {
107                         char buf[32];
108
109                         snprintf (buf, sizeof(buf), "%d", version+1);
110
111                         newname  = name.substr (0, delim+1);
112                         newname += buf;
113                 }
114         }
115
116         return newname;
117
118 }
119
120 XMLNode *
121 find_named_node (const XMLNode& node, string name)
122 {
123         XMLNodeList nlist;
124         XMLNodeConstIterator niter;
125         XMLNode* child;
126
127         nlist = node.children();
128
129         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
130
131                 child = *niter;
132
133                 if (child->name() == name) {
134                         return child;
135                 }
136         }
137
138         return 0;
139 }
140
141 int
142 cmp_nocase (const string& s, const string& s2)
143 {
144         string::const_iterator p = s.begin();
145         string::const_iterator p2 = s2.begin();
146
147         while (p != s.end() && p2 != s2.end()) {
148                 if (toupper(*p) != toupper(*p2)) {
149                         return (toupper(*p) < toupper(*p2)) ? -1 : 1;
150                 }
151                 ++p;
152                 ++p2;
153         }
154
155         return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
156 }
157
158 int
159 touch_file (ustring path)
160 {
161         int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
162         if (fd >= 0) {
163                 close (fd);
164                 return 0;
165         }
166         return 1;
167 }
168
169 ustring
170 region_name_from_path (ustring path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
171 {
172         path = PBD::basename_nosuffix (path);
173
174         if (strip_channels) {
175
176                 /* remove any "?R", "?L" or "?[a-z]" channel identifier */
177
178                 ustring::size_type len = path.length();
179
180                 if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
181                     (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
182
183                         path = path.substr (0, path.length() - 2);
184                 }
185         }
186
187         if (add_channel_suffix) {
188
189                 path += '%';
190
191                 if (total > 2) {
192                         path += (char) ('a' + this_one);
193                 } else {
194                         path += (char) (this_one == 0 ? 'L' : 'R');
195                 }
196         }
197
198         return path;
199 }
200
201 bool
202 path_is_paired (ustring path, ustring& pair_base)
203 {
204         ustring::size_type pos;
205
206         /* remove any leading path */
207
208         if ((pos = path.find_last_of ('/')) != string::npos) {
209                 path = path.substr(pos+1);
210         }
211
212         /* remove filename suffixes etc. */
213
214         if ((pos = path.find_last_of ('.')) != string::npos) {
215                 path = path.substr (0, pos);
216         }
217
218         ustring::size_type len = path.length();
219
220         /* look for possible channel identifier: "?R", "%R", ".L" etc. */
221
222         if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
223             (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
224
225                 pair_base = path.substr (0, len-2);
226                 return true;
227
228         }
229
230         return false;
231 }
232
233 ustring
234 path_expand (ustring path)
235 {
236 #ifdef HAVE_WORDEXP
237         /* Handle tilde and environment variable expansion in session path */
238         string ret = path;
239
240         wordexp_t expansion;
241         switch (wordexp (path.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF)) {
242         case 0:
243                 break;
244         default:
245                 error << string_compose (_("illegal or badly-formed string used for path (%1)"), path) << endmsg;
246                 goto out;
247         }
248
249         if (expansion.we_wordc > 1) {
250                 error << string_compose (_("path (%1) is ambiguous"), path) << endmsg;
251                 goto out;
252         }
253
254         ret = expansion.we_wordv[0];
255   out:
256         wordfree (&expansion);
257         return ret;
258
259 #else
260         return path;
261 #endif
262 }
263
264 #if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS)
265 string
266 CFStringRefToStdString(CFStringRef stringRef)
267 {
268         CFIndex size =
269                 CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
270                 kCFStringEncodingUTF8);
271             char *buf = new char[size];
272
273         std::string result;
274
275         if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) {
276             result = buf;
277         }
278         delete [] buf;
279         return result;
280 }
281 #endif // HAVE_COREAUDIO
282
283 void
284 compute_equal_power_fades (nframes_t nframes, float* in, float* out)
285 {
286         double step;
287
288         step = 1.0/(nframes-1);
289
290         in[0] = 0.0f;
291
292         for (nframes_t i = 1; i < nframes - 1; ++i) {
293                 in[i] = in[i-1] + step;
294         }
295
296         in[nframes-1] = 1.0;
297
298         const float pan_law_attenuation = -3.0f;
299         const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
300
301         for (nframes_t n = 0; n < nframes; ++n) {
302                 float inVal = in[n];
303                 float outVal = 1 - inVal;
304                 out[n] = outVal * (scale * outVal + 1.0f - scale);
305                 in[n] = inVal * (scale * inVal + 1.0f - scale);
306         }
307 }
308
309 EditMode
310 string_to_edit_mode (string str)
311 {
312         if (str == _("Splice")) {
313                 return Splice;
314         } else if (str == _("Slide")) {
315                 return Slide;
316         } else if (str == _("Lock")) {
317                 return Lock;
318         }
319         fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg;
320         /*NOTREACHED*/
321         return Slide;
322 }
323
324 const char*
325 edit_mode_to_string (EditMode mode)
326 {
327         switch (mode) {
328         case Slide:
329                 return _("Slide");
330
331         case Lock:
332                 return _("Lock");
333
334         default:
335         case Splice:
336                 return _("Splice");
337         }
338 }
339
340 SyncSource
341 string_to_sync_source (string str)
342 {
343         if (str == _("MIDI Timecode")) {
344                 return MTC;
345         }
346
347         if (str == _("MIDI Clock")) {
348                 return MIDIClock;
349         }
350
351         if (str == _("JACK")) {
352                 return JACK;
353         }
354
355         fatal << string_compose (_("programming error: unknown sync source string \"%1\""), str) << endmsg;
356         /*NOTREACHED*/
357         return JACK;
358 }
359
360 const char*
361 sync_source_to_string (SyncSource src)
362 {
363         switch (src) {
364         case JACK:
365                 return _("JACK");
366
367         case MTC:
368                 return _("MIDI Timecode");
369
370         case MIDIClock:
371                 return _("MIDI Clock");
372         }
373         /* GRRRR .... stupid, stupid gcc - you can't get here from there, all enum values are handled */
374         return _("JACK");
375 }
376
377 float
378 meter_falloff_to_float (MeterFalloff falloff)
379 {
380         switch (falloff) {
381         case MeterFalloffOff:
382                 return METER_FALLOFF_OFF;
383         case MeterFalloffSlowest:
384                 return METER_FALLOFF_SLOWEST;
385         case MeterFalloffSlow:
386                 return METER_FALLOFF_SLOW;
387         case MeterFalloffMedium:
388                 return METER_FALLOFF_MEDIUM;
389         case MeterFalloffFast:
390                 return METER_FALLOFF_FAST;
391         case MeterFalloffFaster:
392                 return METER_FALLOFF_FASTER;
393         case MeterFalloffFastest:
394                 return METER_FALLOFF_FASTEST;
395         default:
396                 return METER_FALLOFF_FAST;
397         }
398 }
399
400 MeterFalloff
401 meter_falloff_from_float (float val)
402 {
403         if (val == METER_FALLOFF_OFF) {
404                 return MeterFalloffOff;
405         }
406         else if (val <= METER_FALLOFF_SLOWEST) {
407                 return MeterFalloffSlowest;
408         }
409         else if (val <= METER_FALLOFF_SLOW) {
410                 return MeterFalloffSlow;
411         }
412         else if (val <= METER_FALLOFF_MEDIUM) {
413                 return MeterFalloffMedium;
414         }
415         else if (val <= METER_FALLOFF_FAST) {
416                 return MeterFalloffFast;
417         }
418         else if (val <= METER_FALLOFF_FASTER) {
419                 return MeterFalloffFaster;
420         }
421         else {
422                 return MeterFalloffFastest;
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 std::string
502 bool_as_string (bool yn)
503 {
504         return (yn ? "yes" : "no");
505 }
506
507 bool
508 string_is_affirmative (const std::string& str)
509 {
510         /* to be used only with XML data - not intended to handle user input */
511
512         return str == "1" || str == "y" || str == "Y" || (!g_strncasecmp(str.c_str(), "yes", str.length()));
513 }
514
515 const char*
516 native_header_format_extension (HeaderFormat hf, const DataType& type)
517 {
518         if (type == DataType::MIDI) {
519                 return ".mid";
520         }
521         
522         switch (hf) {
523         case BWF:
524                 return ".wav";
525         case WAVE:
526                 return ".wav";
527         case WAVE64:
528                 return ".w64";
529         case CAF:
530                 return ".caf";
531         case AIFF:
532                 return ".aif";
533         case iXML:
534                 return ".ixml";
535         case RF64:
536                 return ".rf64";
537         }
538
539         fatal << string_compose (_("programming error: unknown native header format: %1"), hf);
540         /*NOTREACHED*/
541         return ".wav";
542 }
543
544 bool
545 matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
546 {
547         string bws = basename_nosuffix (path);
548         struct dirent* dentry;
549         struct stat statbuf;
550         DIR* dead;
551         bool ret = false;
552
553         if ((dead = ::opendir (dir.c_str())) == 0) {
554                 error << string_compose (_("cannot open directory %1 (%2)"), dir, strerror (errno)) << endl;
555                 return false;
556         }
557         
558         while ((dentry = ::readdir (dead)) != 0) {
559                 
560                 /* avoid '.' and '..' */
561                 
562                 if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
563                     (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
564                         continue;
565                 }
566         
567                 string fullpath = Glib::build_filename (dir, dentry->d_name);
568
569                 if (::stat (fullpath.c_str(), &statbuf)) {
570                         continue;
571                 }
572                 
573                 if (!S_ISREG (statbuf.st_mode)) {
574                         continue;
575                 }
576
577                 string bws2 = basename_nosuffix (dentry->d_name);
578                 
579                 if (bws2 == bws) {
580                         ret = true;
581                         break;
582                 }
583         }
584
585         ::closedir (dead);
586         return ret;
587 }
588
589 extern "C" {
590         void c_stacktrace() { stacktrace (cerr); }
591 }