add missing xml closers, add fix for folder bug, add roundoff fixups
[goodguy/history.git] / cinelerra-5.1 / guicast / units.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcwindowbase.inc"
23 #include "units.h"
24 #include "language.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <ctype.h>
29
30 float* DB::topower = 0;
31 float* DB::topower_base = 0;
32 int* Freq::freqtable = 0;
33
34
35 DB::DB(float infinitygain)
36 {
37         this->infinitygain = infinitygain;
38         if(!topower) { // db to power table
39                 topower_base = new float[(MAXGAIN - INFINITYGAIN) * 10 + 1];
40                 topower = topower_base + -INFINITYGAIN * 10;
41                 for(int i = INFINITYGAIN * 10; i <= MAXGAIN * 10; i++) {
42                         topower[i] = pow(10, (float)i / 10 / 20);
43 //printf("%f %f\n", (float)i/10, topower[i]);
44                 }
45                 topower[INFINITYGAIN * 10] = 0;   // infinity gain
46         }
47         db = 0;
48 }
49
50 float DB::fromdb_table()
51 {
52         return db = topower[(int)(db*10)];
53 }
54
55 float DB::fromdb_table(float db)
56 {
57         if(db > MAXGAIN) db = MAXGAIN;
58         if(db <= INFINITYGAIN) return 0;
59         return db = topower[(int)(db*10)];
60 }
61
62 float DB::fromdb()
63 {
64         return pow(10, db/20);
65 }
66
67 float DB::fromdb(float db)
68 {
69         return pow(10, db/20);
70 }
71
72 // set db to the power given using a formula
73 float DB::todb(float power)
74 {
75         float db;
76         if(power > 0) {
77                 db = 20 * log10(power);
78                 if(db < -100) db = -100;
79         }
80         else
81                 db = -100;
82         return db;
83 }
84
85
86 Freq::Freq()
87 {
88         if( !freqtable ) init_table();
89         freq = 0;
90 }
91
92 Freq::Freq(const Freq& oldfreq)
93 {
94         this->freq = oldfreq.freq;
95 }
96
97 void Freq::init_table()
98 {
99         freqtable = new int[TOTALFREQS + 1];
100 // starting frequency
101         double freq1 = 27.5, freq2 = 55;
102 // Some number divisable by three.  This depends on the value of TOTALFREQS
103         int scale = 105;
104
105         freqtable[0] = 0;
106         for(int i = 1, j = 0; i <= TOTALFREQS; i++, j++) {
107                 freqtable[i] = (int)(freq1 + (freq2 - freq1) / scale * j + 0.5);
108 //printf("Freq::init_table %d\n", freqtable[i]);
109                 if(j >= scale) {
110                         freq1 = freq2;
111                         freq2 *= 2;
112                         j = 0;
113                 }
114         }
115 }
116
117 int Freq::fromfreq()
118 {
119         int i = 0;
120         if( !freqtable ) init_table();
121         while( i<TOTALFREQS && freqtable[i]<freq ) ++i;
122         return i;
123 };
124
125 int Freq::fromfreq(int index)
126 {
127         int i = 0;
128         if( !freqtable ) init_table();
129         while( i<TOTALFREQS && freqtable[i]<index ) ++i;
130         return i;
131 };
132
133 int Freq::tofreq(int index)
134 {
135         if( !freqtable ) init_table();
136         if(index >= TOTALFREQS) index = TOTALFREQS - 1;
137         return freqtable[index];
138 }
139
140 Freq& Freq::operator++()
141 {
142         if(freq < TOTALFREQS) ++freq;
143         return *this;
144 }
145
146 Freq& Freq::operator--()
147 {
148         if(freq > 0) --freq;
149         return *this;
150 }
151
152 int Freq::operator>(Freq &newfreq) { return freq > newfreq.freq; }
153 int Freq::operator<(Freq &newfreq) { return freq < newfreq.freq; }
154 Freq& Freq::operator=(const Freq &newfreq) { freq = newfreq.freq; return *this; }
155 int Freq::operator=(const int newfreq) { freq = newfreq; return newfreq; }
156 int Freq::operator!=(Freq &newfreq) { return freq != newfreq.freq; }
157 int Freq::operator==(Freq &newfreq) { return freq == newfreq.freq; }
158 int Freq::operator==(int newfreq) { return freq == newfreq; }
159
160 // give text representation as time
161 char* Units::totext(char *text, double seconds, int time_format,
162                         int sample_rate, float frame_rate, float frames_per_foot)
163 {
164         int64_t hour, feet, frame;
165         int minute, second, thousandths;
166
167         switch(time_format) {
168         case TIME_SECONDS: {
169 // add 1.0e-6 to prevent round off truncation from glitching a bunch of digits
170                 seconds = fabs(seconds) + 1.0e-6;
171                 second = seconds;
172                 seconds -= (int64_t)seconds;
173                 thousandths = (int64_t)(seconds*1000) % 1000;
174                 sprintf(text, "%04d.%03d", second, thousandths);
175                 break; }
176
177         case TIME_HMS: {
178                 seconds = fabs(seconds) + 1.0e-6;
179                 hour = seconds/3600;
180                 minute = seconds/60 - hour*60;
181                 second = seconds - (hour*3600 + minute*60);
182                 seconds -= (int64_t)seconds;
183                 thousandths = (int64_t)(seconds*1000) % 1000;
184                 sprintf(text, "%d:%02d:%02d.%03d",
185                         (int)hour, minute, second, thousandths);
186                 break; }
187
188         case TIME_HMS2: {
189                 seconds = fabs(seconds) + 1.0e-6;
190                 hour = seconds/3600;
191                 minute = seconds/60 - hour*60;
192                 second = seconds - (hour*3600 + minute*60);
193                 sprintf(text, "%d:%02d:%02d", (int)hour, minute, second);
194                 break; }
195
196         case TIME_HMS3: {
197                 seconds = fabs(seconds) + 1.0e-6;
198                 hour = seconds/3600;
199                 minute = seconds/60 - hour*60;
200                 second = seconds - (hour*3600 + minute*60);
201                 sprintf(text, "%02d:%02d:%02d", (int)hour, minute, second);
202                 break; }
203
204         case TIME_HMSF: {
205                 seconds = fabs(seconds) + 1.0e-6;
206                 hour = seconds/3600;
207                 minute = seconds/60 - hour*60;
208                 second = seconds - (hour*3600 + minute*60);
209                 seconds -= (int64_t)seconds;
210 //              frame = round(frame_rate * (seconds-(int)seconds));
211                 frame = frame_rate*seconds + 1.0e-6;
212                 sprintf(text, "%01d:%02d:%02d:%02d", (int)hour, minute, second, (int)frame);
213                 break; }
214
215         case TIME_SAMPLES: {
216                 sprintf(text, "%09jd", to_int64(seconds * sample_rate));
217                 break; }
218
219         case TIME_SAMPLES_HEX: {
220                 sprintf(text, "%08jx", to_int64(seconds * sample_rate));
221                 break; }
222
223         case TIME_FRAMES: {
224                 frame = to_int64(seconds * frame_rate);
225                 sprintf(text, "%06jd", frame);
226                 break; }
227
228         case TIME_FEET_FRAMES: {
229                 frame = to_int64(seconds * frame_rate);
230                 feet = (int64_t)(frame / frames_per_foot);
231                 sprintf(text, "%05jd-%02jd", feet,
232                         (int64_t)(frame - feet * frames_per_foot));
233                 break; }
234
235         case TIME_MS1: {
236                 seconds = fabs(seconds) + 1.0e-6;
237                 minute = seconds/60;
238                 second = seconds - minute*60;
239                 sprintf(text, "%d:%02d", minute, second);
240                 break; }
241
242         case TIME_MS2: {
243                 int sign = seconds >= 0 ? '+' : '-';
244                 seconds = fabs(seconds) + 1.0e-6;
245                 minute = seconds/60;
246                 second = seconds - minute*60;
247                 sprintf(text, "%c%d:%02d", sign, minute, second);
248                 break; }
249         default: {
250                 *text = 0;
251                 break; }
252         }
253         return text;
254 }
255
256
257 // give text representation as time
258 char* Units::totext(char *text, int64_t samples, int samplerate,
259                 int time_format, float frame_rate, float frames_per_foot)
260 {
261         return totext(text, (double)samples/samplerate, time_format,
262                         samplerate, frame_rate, frames_per_foot);
263 }
264
265 int64_t Units::get_int64(const char *&bp)
266 {
267         char string[BCTEXTLEN], *sp=&string[0];
268         const char *cp = bp;
269         for( int j=0; j<10 && isdigit(*cp); ++j ) *sp++ = *cp++;
270         *sp = 0;  bp = cp;
271         return atol(string);
272 }
273
274 double Units::get_double(const char *&bp)
275 {
276         char string[BCTEXTLEN], *sp=&string[0];
277         const char *cp = bp;
278         for( int j=0; j<10 && isdigit(*cp); ++j ) *sp++ = *cp++;
279         if( *cp == '.' ) {
280                 *sp++ = *cp++;
281                 for( int j=0; j<10 && isdigit(*cp); ++j ) *sp++ = *cp++;
282         }
283         *sp = 0;  bp = cp;
284         return strtod(string,0);
285 }
286
287 void Units::skip_seperators(const char *&bp)
288 {
289         const char *cp = bp;
290         for( int j=0; j<10 && *cp && !isdigit(*cp); ++j ) ++cp;
291         bp = cp;
292 }
293
294 int64_t Units::fromtext(const char *text, int samplerate, int time_format,
295                         float frame_rate, float frames_per_foot)
296 {
297         int64_t hours, total_samples;
298         int minutes, frames, feet;
299         double seconds, total_seconds;
300         int sign = 1;
301
302         switch(time_format) {
303         case TIME_SECONDS: {
304                 total_seconds = get_double(text);
305                 break; }
306
307         case TIME_HMS:
308         case TIME_HMS2:
309         case TIME_HMS3: {
310                 hours = get_int64(text);    skip_seperators(text);
311                 minutes = get_int64(text);  skip_seperators(text);
312                 seconds = get_int64(text);
313                 total_seconds = seconds + minutes*60 + hours*3600;
314                 break; }
315
316         case TIME_HMSF: {
317                 hours = get_int64(text);    skip_seperators(text);
318                 minutes = get_int64(text);  skip_seperators(text);
319                 seconds = get_int64(text);  skip_seperators(text);
320                 frames = get_int64(text);
321                 total_seconds = frames/frame_rate + seconds + minutes*60 + hours*3600;
322                 break; }
323
324         case TIME_SAMPLES: {
325                 return get_int64(text); }
326
327         case TIME_SAMPLES_HEX: {
328                 sscanf(text, "%jx", &total_samples);
329                 return total_samples; }
330
331         case TIME_FRAMES: {
332                 total_seconds = get_double(text) / frame_rate;
333                 break; }
334
335         case TIME_FEET_FRAMES: {
336                 feet = get_int64(text);    skip_seperators(text);
337                 frames = get_int64(text);
338                 total_seconds = (feet*frames_per_foot + frames) / frame_rate;
339                 break; }
340
341         case TIME_MS2: {
342                 switch( *text ) {
343                 case '+':  sign = 1;   ++text;  break;
344                 case '-':  sign = -1;  ++text;  break;
345                 } } // fall through
346         case TIME_MS1: {
347                 minutes = get_int64(text);   skip_seperators(text);
348                 seconds = get_double(text);
349                 total_seconds = sign * (seconds + minutes*60);
350                 break; }
351         default: {
352                 total_seconds = 0;
353                 break; }
354         }
355
356         total_samples = total_seconds * samplerate;
357         return total_samples;
358 }
359
360 double Units::text_to_seconds(const char *text, int samplerate, int time_format,
361                                 float frame_rate, float frames_per_foot)
362 {
363         return (double)fromtext(text, samplerate, time_format,
364                                 frame_rate, frames_per_foot) / samplerate;
365 }
366
367
368
369
370 int Units::timeformat_totype(char *tcf)
371 {
372         if (!strcmp(tcf,TIME_SECONDS__STR)) return(TIME_SECONDS);
373         if (!strcmp(tcf,TIME_HMS__STR)) return(TIME_HMS);
374         if (!strcmp(tcf,TIME_HMS2__STR)) return(TIME_HMS2);
375         if (!strcmp(tcf,TIME_HMS3__STR)) return(TIME_HMS3);
376         if (!strcmp(tcf,TIME_HMSF__STR)) return(TIME_HMSF);
377         if (!strcmp(tcf,TIME_SAMPLES__STR)) return(TIME_SAMPLES);
378         if (!strcmp(tcf,TIME_SAMPLES_HEX__STR)) return(TIME_SAMPLES_HEX);
379         if (!strcmp(tcf,TIME_FRAMES__STR)) return(TIME_FRAMES);
380         if (!strcmp(tcf,TIME_FEET_FRAMES__STR)) return(TIME_FEET_FRAMES);
381         return(-1);
382 }
383
384
385 float Units::toframes(int64_t samples, int sample_rate, float framerate)
386 {
387         return (double)samples/sample_rate * framerate;
388 } // give position in frames
389
390 int64_t Units::toframes_round(int64_t samples, int sample_rate, float framerate)
391 {
392 // used in editing
393         float result_f = (float)samples / sample_rate * framerate;
394         int64_t result_l = (int64_t)(result_f + 0.5);
395         return result_l;
396 }
397
398 double Units::fix_framerate(double value)
399 {
400         if(value > 29.5 && value < 30)
401                 value = (double)30000 / (double)1001;
402         else if(value > 59.5 && value < 60)
403                 value = (double)60000 / (double)1001;
404         else if(value > 23.5 && value < 24)
405                 value = (double)24000 / (double)1001;
406         return value;
407 }
408
409 double Units::atoframerate(const char *text)
410 {
411         double result = get_double(text);
412         return fix_framerate(result);
413 }
414
415
416 int64_t Units::tosamples(double frames, int sample_rate, float framerate)
417 {
418         double result = frames/framerate * sample_rate;
419         if(result - (int)result >= 1.0e-6 ) result += 1;
420         return (int64_t)result;
421 } // give position in samples
422
423
424 float Units::xy_to_polar(int x, int y)
425 {
426         float angle = 0.;
427         if(x > 0 && y <= 0)
428                 angle = atan((float)-y / x) / (2*M_PI) * 360;
429         else if(x < 0 && y <= 0)
430                 angle = 180 - atan((float)-y / -x) / (2*M_PI) * 360;
431         else if(x < 0 && y > 0)
432                 angle = 180 - atan((float)-y / -x) / (2*M_PI) * 360;
433         else if(x > 0 && y > 0)
434                 angle = 360 + atan((float)-y / x) / (2*M_PI) * 360;
435         else if(x == 0 && y < 0)
436                 angle = 90;
437         else if(x == 0 && y > 0)
438                 angle = 270;
439         else if(x == 0 && y == 0)
440                 angle = 0;
441         return angle;
442 }
443
444 void Units::polar_to_xy(float angle, int radius, int &x, int &y)
445 {
446         if( angle < 0 )
447                 angle += ((int)(-angle)/360 + 1) * 360;
448         else if( angle >= 360 )
449                 angle -= ((int)(angle)/360) * 360;
450
451         x = (int)(cos(angle / 360 * (2*M_PI)) * radius);
452         y = (int)(-sin(angle / 360 * (2*M_PI)) * radius);
453 }
454
455 int64_t Units::round(double result)
456 {
457         return (int64_t)(result < 0 ? result - 0.5 : result + 0.5);
458 }
459
460 float Units::quantize10(float value)
461 {
462         int64_t temp = (int64_t)(value*10 + 0.5);
463         return temp/10.;
464 }
465
466 float Units::quantize(float value, float precision)
467 {
468         int64_t temp = (int64_t)(value/precision + 0.5);
469         return temp*precision;
470 }
471
472 int64_t Units::to_int64(double result)
473 {
474 // This must round up if result is one sample within ceiling.
475 // Sampling rates below 48000 may cause more problems.
476         return (int64_t)(result < 0 ? (result - 0.005) : (result + 0.005));
477 }
478
479 const char* Units::print_time_format(int time_format, char *string)
480 {
481         const char *fmt = "Unknown";
482         switch(time_format) {
483         case TIME_HMS:         fmt = TIME_HMS_TEXT;                   break;
484         case TIME_HMSF:        fmt = TIME_HMSF_TEXT;                  break;
485         case TIME_SAMPLES:     fmt = TIME_SAMPLES_TEXT;               break;
486         case TIME_SAMPLES_HEX: fmt = TIME_SAMPLES_HEX_TEXT;           break;
487         case TIME_FRAMES:      fmt = TIME_FRAMES_TEXT;                break;
488         case TIME_FEET_FRAMES: fmt = TIME_FEET_FRAMES_TEXT;           break;
489         case TIME_HMS2:
490         case TIME_HMS3:        fmt = TIME_HMS3_TEXT;                  break;
491         case TIME_SECONDS:     fmt = TIME_SECONDS_TEXT;               break;
492         case TIME_MS1:
493         case TIME_MS2:         fmt = TIME_MS2_TEXT;                   break;
494         }
495         return strcpy(string,fmt);
496 }
497
498 int Units::text_to_format(const char *string)
499 {
500         if(!strcmp(string, TIME_HMS_TEXT)) return TIME_HMS;
501         if(!strcmp(string, TIME_HMSF_TEXT)) return TIME_HMSF;
502         if(!strcmp(string, TIME_SAMPLES_TEXT)) return TIME_SAMPLES;
503         if(!strcmp(string, TIME_SAMPLES_HEX_TEXT)) return TIME_SAMPLES_HEX;
504         if(!strcmp(string, TIME_FRAMES_TEXT)) return TIME_FRAMES;
505         if(!strcmp(string, TIME_FEET_FRAMES_TEXT)) return TIME_FEET_FRAMES;
506         if(!strcmp(string, TIME_HMS3_TEXT)) return TIME_HMS3;
507         if(!strcmp(string, TIME_SECONDS_TEXT)) return TIME_SECONDS;
508         if(!strcmp(string, TIME_MS2_TEXT)) return TIME_MS2;
509         return TIME_HMS;
510 }
511
512 char* Units::size_totext(int64_t bytes, char *text)
513 {
514         char string[BCTEXTLEN];
515         static const char *sz[] = { "bytes", "KB", "MB", "GB", "TB" };
516
517         int i = (sizeof(sz) / sizeof(sz[0]));
518         while( --i > 0 && bytes < ((int64_t)1 << (10*i)) );
519
520         if( i > 0 ) {
521                 bytes >>= 10*(i-1);
522                 int frac = bytes % 1000;
523                 sprintf(string, "%jd", bytes/1000);
524                 if( bytes > 1000 ) punctuate(string);
525                 sprintf(text, "%s.%03d %s", string, frac, sz[i]);
526         }
527         else {
528                 sprintf(string, "%jd", bytes);
529                 if( bytes > 1000 ) punctuate(string);
530                 sprintf(text, "%s %s", string, sz[i]);
531         }
532         return text;
533 }
534
535
536 #undef BYTE_ORDER
537 #define BYTE_ORDER ((*(const uint32_t*)"a   ") & 0x00000001)
538
539 void* Units::int64_to_ptr(uint64_t value)
540 {
541         unsigned char *value_dissected = (unsigned char*)&value;
542         void *result;
543         unsigned char *data = (unsigned char*)&result;
544
545 // Must be done behind the compiler's back
546         if(sizeof(void*) == 4) {
547                 if(!BYTE_ORDER) {
548                         data[0] = value_dissected[4];
549                         data[1] = value_dissected[5];
550                         data[2] = value_dissected[6];
551                         data[3] = value_dissected[7];
552                 }
553                 else {
554                         data[0] = value_dissected[0];
555                         data[1] = value_dissected[1];
556                         data[2] = value_dissected[2];
557                         data[3] = value_dissected[3];
558                 }
559         }
560         else {
561                 data[0] = value_dissected[0];
562                 data[1] = value_dissected[1];
563                 data[2] = value_dissected[2];
564                 data[3] = value_dissected[3];
565                 data[4] = value_dissected[4];
566                 data[5] = value_dissected[5];
567                 data[6] = value_dissected[6];
568                 data[7] = value_dissected[7];
569         }
570         return result;
571 }
572
573 uint64_t Units::ptr_to_int64(void *ptr)
574 {
575         unsigned char *ptr_dissected = (unsigned char*)&ptr;
576         int64_t result = 0;
577         unsigned char *data = (unsigned char*)&result;
578 // Don't do this at home.
579         if(sizeof(void*) == 4) {
580                 if(!BYTE_ORDER) {
581                         data[4] = ptr_dissected[0];
582                         data[5] = ptr_dissected[1];
583                         data[6] = ptr_dissected[2];
584                         data[7] = ptr_dissected[3];
585                 }
586                 else {
587                         data[0] = ptr_dissected[0];
588                         data[1] = ptr_dissected[1];
589                         data[2] = ptr_dissected[2];
590                         data[3] = ptr_dissected[3];
591                 }
592         }
593         else {
594                 data[0] = ptr_dissected[0];
595                 data[1] = ptr_dissected[1];
596                 data[2] = ptr_dissected[2];
597                 data[3] = ptr_dissected[3];
598                 data[4] = ptr_dissected[4];
599                 data[5] = ptr_dissected[5];
600                 data[6] = ptr_dissected[6];
601                 data[7] = ptr_dissected[7];
602         }
603         return result;
604 }
605
606 const char* Units::format_to_separators(int time_format)
607 {
608         switch(time_format) {
609                 case TIME_SECONDS:     return "0000.000";
610                 case TIME_HMS:         return "0:00:00.000";
611                 case TIME_HMS2:        return "0:00:00";
612                 case TIME_HMS3:        return "00:00:00";
613                 case TIME_HMSF:        return "0:00:00:00";
614                 case TIME_SAMPLES:     return 0;
615                 case TIME_SAMPLES_HEX: return 0;
616                 case TIME_FRAMES:      return 0;
617                 case TIME_FEET_FRAMES: return "00000-00";
618                 case TIME_MS1:         return "0:00";
619                 case TIME_MS2:         return "+0:00";
620         }
621         return 0;
622 }
623
624 void Units::punctuate(char *string)
625 {
626         int len = strlen(string);
627         int commas = (len - 1) / 3;
628         for(int i = len + commas, j = len, k; j >= 0 && i >= 0; i--, j--) {
629                 k = (len - j - 1) / 3;
630                 if(k * 3 == len - j - 1 && j != len - 1 && string[j] != 0) {
631                         string[i--] = ',';
632                 }
633                 string[i] = string[j];
634         }
635 }
636
637 void Units::fix_double(double *x)
638 {
639         *x = *x;
640 }
641
642