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