1 /*
2 * Copyright (C) 2017 Paul Davis <paul@linuxaudiosystems.com>
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 along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define Timecode_IS_AROUND_ZERO(sm) (!(sm).frames && !(sm).seconds && !(sm).minutes && !(sm).hours)
20 #define Timecode_IS_ZERO(sm) (!(sm).frames && !(sm).seconds && !(sm).minutes && !(sm).hours && !(sm.subframes))
21
22 #include <math.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "temporal/time.h"
27
28 namespace Timecode {
29
30 double Time::default_rate = 30.0;
31
32
33 /** Increment @a timecode by exactly one sample (keep subframes value).
34 * Realtime safe.
35 * @return true if seconds wrap.
36 */
37 Wrap
increment(Time & timecode,uint32_t subframes_per_frame)38 increment (Time& timecode, uint32_t subframes_per_frame)
39 {
40 Wrap wrap = NONE;
41
42 if (timecode.negative) {
43 if (Timecode_IS_AROUND_ZERO (timecode) && timecode.subframes) {
44 // We have a zero transition involving only subframes
45 timecode.subframes = subframes_per_frame - timecode.subframes;
46 timecode.negative = false;
47 return SECONDS;
48 }
49
50 timecode.negative = false;
51 wrap = decrement (timecode, subframes_per_frame);
52 if (!Timecode_IS_ZERO (timecode)) {
53 timecode.negative = true;
54 }
55 return wrap;
56 }
57
58 switch ((int)ceil (timecode.rate)) {
59 case 24:
60 if (timecode.frames == 23) {
61 timecode.frames = 0;
62 wrap = SECONDS;
63 }
64 break;
65 case 25:
66 if (timecode.frames == 24) {
67 timecode.frames = 0;
68 wrap = SECONDS;
69 }
70 break;
71 case 30:
72 if (timecode.drop) {
73 if (timecode.frames == 29) {
74 if (((timecode.minutes + 1) % 10) && (timecode.seconds == 59)) {
75 timecode.frames = 2;
76 }
77 else {
78 timecode.frames = 0;
79 }
80 wrap = SECONDS;
81 }
82 } else {
83
84 if (timecode.frames == 29) {
85 timecode.frames = 0;
86 wrap = SECONDS;
87 }
88 }
89 break;
90 case 60:
91 if (timecode.frames == 59) {
92 timecode.frames = 0;
93 wrap = SECONDS;
94 }
95 break;
96 }
97
98 if (wrap == SECONDS) {
99 if (timecode.seconds == 59) {
100 timecode.seconds = 0;
101 wrap = MINUTES;
102 if (timecode.minutes == 59) {
103 timecode.minutes = 0;
104 wrap = HOURS;
105 timecode.hours++;
106 } else {
107 timecode.minutes++;
108 }
109 } else {
110 timecode.seconds++;
111 }
112 } else {
113 timecode.frames++;
114 }
115
116 return wrap;
117 }
118
119
120 /** Decrement @a timecode by exactly one sample (keep subframes value)
121 * Realtime safe.
122 * @return true if seconds wrap. */
123 Wrap
decrement(Time & timecode,uint32_t subframes_per_frame)124 decrement (Time& timecode, uint32_t subframes_per_frame)
125 {
126 Wrap wrap = NONE;
127
128 if (timecode.negative || Timecode_IS_ZERO (timecode)) {
129 timecode.negative = false;
130 wrap = increment (timecode, subframes_per_frame);
131 timecode.negative = true;
132 return wrap;
133 } else if (Timecode_IS_AROUND_ZERO (timecode) && timecode.subframes) {
134 // We have a zero transition involving only subframes
135 timecode.subframes = subframes_per_frame - timecode.subframes;
136 timecode.negative = true;
137 return SECONDS;
138 }
139
140 switch ((int)ceil (timecode.rate)) {
141 case 24:
142 if (timecode.frames == 0) {
143 timecode.frames = 23;
144 wrap = SECONDS;
145 }
146 break;
147 case 25:
148 if (timecode.frames == 0) {
149 timecode.frames = 24;
150 wrap = SECONDS;
151 }
152 break;
153 case 30:
154 if (timecode.drop) {
155 if ((timecode.minutes % 10) && (timecode.seconds == 0)) {
156 if (timecode.frames <= 2) {
157 timecode.frames = 29;
158 wrap = SECONDS;
159 }
160 } else if (timecode.frames == 0) {
161 timecode.frames = 29;
162 wrap = SECONDS;
163 }
164
165 } else {
166 if (timecode.frames == 0) {
167 timecode.frames = 29;
168 wrap = SECONDS;
169 }
170 }
171 break;
172 case 60:
173 if (timecode.frames == 0) {
174 timecode.frames = 59;
175 wrap = SECONDS;
176 }
177 break;
178 }
179
180 if (wrap == SECONDS) {
181 if (timecode.seconds == 0) {
182 timecode.seconds = 59;
183 wrap = MINUTES;
184 if (timecode.minutes == 0) {
185 timecode.minutes = 59;
186 wrap = HOURS;
187 timecode.hours--;
188 }
189 else {
190 timecode.minutes--;
191 }
192 } else {
193 timecode.seconds--;
194 }
195 } else {
196 timecode.frames--;
197 }
198
199 if (Timecode_IS_ZERO (timecode)) {
200 timecode.negative = false;
201 }
202
203 return wrap;
204 }
205
206
207 /** Go to lowest absolute subframe value in this sample (set to 0 :-)) */
208 void
frames_floot(Time & timecode)209 frames_floot (Time& timecode)
210 {
211 timecode.subframes = 0;
212 if (Timecode_IS_ZERO (timecode)) {
213 timecode.negative = false;
214 }
215 }
216
217
218 /** Increment @a timecode by one subframe */
219 Wrap
increment_subframes(Time & timecode,uint32_t subframes_per_frame)220 increment_subframes (Time& timecode, uint32_t subframes_per_frame)
221 {
222 Wrap wrap = NONE;
223
224 if (timecode.negative) {
225 timecode.negative = false;
226 wrap = decrement_subframes (timecode, subframes_per_frame);
227 if (!Timecode_IS_ZERO (timecode)) {
228 timecode.negative = true;
229 }
230 return wrap;
231 }
232
233 timecode.subframes++;
234 if (timecode.subframes >= subframes_per_frame) {
235 timecode.subframes = 0;
236 increment (timecode, subframes_per_frame);
237 return FRAMES;
238 }
239 return NONE;
240 }
241
242
243 /** Decrement @a timecode by one subframe */
244 Wrap
decrement_subframes(Time & timecode,uint32_t subframes_per_frame)245 decrement_subframes (Time& timecode, uint32_t subframes_per_frame)
246 {
247 Wrap wrap = NONE;
248
249 if (timecode.negative) {
250 timecode.negative = false;
251 wrap = increment_subframes (timecode, subframes_per_frame);
252 timecode.negative = true;
253 return wrap;
254 }
255
256 if (timecode.subframes <= 0) {
257 timecode.subframes = 0;
258 if (Timecode_IS_ZERO (timecode)) {
259 timecode.negative = true;
260 timecode.subframes = 1;
261 return FRAMES;
262 } else {
263 decrement (timecode, subframes_per_frame);
264 timecode.subframes = 79;
265 return FRAMES;
266 }
267 } else {
268 timecode.subframes--;
269 if (Timecode_IS_ZERO (timecode)) {
270 timecode.negative = false;
271 }
272 return NONE;
273 }
274 }
275
276
277 /** Go to next whole second (frames == 0 or frames == 2) */
278 Wrap
increment_seconds(Time & timecode,uint32_t subframes_per_frame)279 increment_seconds (Time& timecode, uint32_t subframes_per_frame)
280 {
281 Wrap wrap = NONE;
282
283 // Clear subframes
284 frames_floot (timecode);
285
286 if (timecode.negative) {
287 // Wrap second if on second boundary
288 wrap = increment (timecode, subframes_per_frame);
289 // Go to lowest absolute frame value
290 seconds_floor (timecode);
291 if (Timecode_IS_ZERO (timecode)) {
292 timecode.negative = false;
293 }
294 } else {
295 // Go to highest possible frame in this second
296 switch ((int)ceil (timecode.rate)) {
297 case 24:
298 timecode.frames = 23;
299 break;
300 case 25:
301 timecode.frames = 24;
302 break;
303 case 30:
304 timecode.frames = 29;
305 break;
306 case 60:
307 timecode.frames = 59;
308 break;
309 }
310
311 // Increment by one frame
312 wrap = increment (timecode, subframes_per_frame);
313 }
314
315 return wrap;
316 }
317
318
319 /** Go to lowest (absolute) frame value in this second
320 * Doesn't care about positive/negative */
321 void
seconds_floor(Time & timecode)322 seconds_floor (Time& timecode)
323 {
324 // Clear subframes
325 frames_floot (timecode);
326
327 // Go to lowest possible frame in this second
328 switch ((int)ceil (timecode.rate)) {
329 case 24:
330 case 25:
331 case 30:
332 case 60:
333 if (!(timecode.drop)) {
334 timecode.frames = 0;
335 } else {
336 if ((timecode.minutes % 10) && (timecode.seconds == 0)) {
337 timecode.frames = 2;
338 } else {
339 timecode.frames = 0;
340 }
341 }
342 break;
343 }
344
345 if (Timecode_IS_ZERO (timecode)) {
346 timecode.negative = false;
347 }
348 }
349
350
351 /** Go to next whole minute (seconds == 0, frames == 0 or frames == 2) */
352 Wrap
increment_minutes(Time & timecode,uint32_t subframes_per_frame)353 increment_minutes (Time& timecode, uint32_t subframes_per_frame)
354 {
355 Wrap wrap = NONE;
356
357 // Clear subframes
358 frames_floot (timecode);
359
360 if (timecode.negative) {
361 // Wrap if on minute boundary
362 wrap = increment_seconds (timecode, subframes_per_frame);
363 // Go to lowest possible value in this minute
364 minutes_floor (timecode);
365 } else {
366 // Go to highest possible second
367 timecode.seconds = 59;
368 // Wrap minute by incrementing second
369 wrap = increment_seconds (timecode, subframes_per_frame);
370 }
371
372 return wrap;
373 }
374
375
376 /** Go to lowest absolute value in this minute */
377 void
minutes_floor(Time & timecode)378 minutes_floor (Time& timecode)
379 {
380 // Go to lowest possible second
381 timecode.seconds = 0;
382 // Go to lowest possible frame
383 seconds_floor (timecode);
384
385 if (Timecode_IS_ZERO (timecode)) {
386 timecode.negative = false;
387 }
388 }
389
390
391 /** Go to next whole hour (minute = 0, second = 0, frame = 0) */
392 Wrap
increment_hours(Time & timecode,uint32_t subframes_per_frame)393 increment_hours (Time& timecode, uint32_t subframes_per_frame)
394 {
395 Wrap wrap = NONE;
396
397 // Clear subframes
398 frames_floot (timecode);
399
400 if (timecode.negative) {
401 // Wrap if on hour boundary
402 wrap = increment_minutes (timecode, subframes_per_frame);
403 // Go to lowest possible value in this hour
404 hours_floor(timecode);
405 } else {
406 timecode.minutes = 59;
407 wrap = increment_minutes (timecode, subframes_per_frame);
408 }
409
410 return wrap;
411 }
412
413
414 /** Go to lowest absolute value in this hour */
415 void
hours_floor(Time & timecode)416 hours_floor(Time& timecode)
417 {
418 timecode.minutes = 0;
419 timecode.seconds = 0;
420 timecode.frames = 0;
421 timecode.subframes = 0;
422
423 if (Timecode_IS_ZERO (timecode)) {
424 timecode.negative = false;
425 }
426 }
427
428 double
timecode_to_frames_per_second(TimecodeFormat t)429 timecode_to_frames_per_second(TimecodeFormat t)
430 {
431 switch (t) {
432 case timecode_23976:
433 return (24000.0/1001.0); //23.976;
434
435 break;
436 case timecode_24:
437 return 24;
438
439 break;
440 case timecode_24976:
441 return (25000.0/1001.0); //24.976;
442
443 break;
444 case timecode_25:
445 return 25;
446
447 break;
448 case timecode_2997:
449 return (30000.0/1001.0); //29.97;
450
451 break;
452 case timecode_2997drop:
453 return (30000.0/1001.0); //29.97;
454
455 break;
456 case timecode_2997000:
457 return 29.97;
458
459 break;
460 case timecode_2997000drop:
461 return 29.97;
462
463 break;
464 case timecode_30:
465 return 30;
466
467 break;
468 case timecode_30drop:
469 return 30;
470
471 break;
472 case timecode_5994:
473 return (60000.0/1001.0); //59.94;
474
475 break;
476 case timecode_60:
477 return 60;
478
479 break;
480 default:
481 //std::cerr << "Editor received unexpected timecode type" << std::endl;
482 break;
483 }
484 return 30.0;
485 }
486
487 bool
timecode_has_drop_frames(TimecodeFormat t)488 timecode_has_drop_frames(TimecodeFormat t)
489 {
490 switch (t) {
491 case timecode_23976:
492 return false;
493
494 break;
495 case timecode_24:
496 return false;
497
498 break;
499 case timecode_24976:
500 return false;
501
502 break;
503 case timecode_25:
504 return false;
505
506 break;
507 case timecode_2997:
508 return false;
509
510 break;
511 case timecode_2997drop:
512 return true;
513
514 break;
515 case timecode_2997000:
516 return false;
517
518 break;
519 case timecode_2997000drop:
520 return true;
521
522 break;
523 case timecode_30:
524 return false;
525
526 break;
527 case timecode_30drop:
528 return true;
529
530 break;
531 case timecode_5994:
532 return false;
533
534 break;
535 case timecode_60:
536 return false;
537
538 break;
539 default:
540 //error << "Editor received unexpected timecode type" << endmsg;
541 break;
542 }
543
544 return false;
545 }
546
547 std::string
timecode_format_name(TimecodeFormat const t)548 timecode_format_name (TimecodeFormat const t)
549 {
550 switch (t) {
551 case timecode_23976:
552 return "23.98";
553
554 break;
555 case timecode_24:
556 return "24";
557
558 break;
559 case timecode_24976:
560 return "24.98";
561
562 break;
563 case timecode_25:
564 return "25";
565
566 break;
567 case timecode_2997000:
568 case timecode_2997:
569 return "29.97";
570
571 break;
572 case timecode_2997000drop:
573 case timecode_2997drop:
574 return "29.97 drop";
575
576 break;
577 case timecode_30:
578 return "30";
579
580 break;
581 case timecode_30drop:
582 return "30 drop";
583
584 break;
585 case timecode_5994:
586 return "59.94";
587
588 break;
589 case timecode_60:
590 return "60";
591
592 break;
593 default:
594 break;
595 }
596
597 return "??";
598 }
599
timecode_format_time(Timecode::Time TC)600 std::string timecode_format_time (Timecode::Time TC)
601 {
602 char buf[32];
603 if (TC.negative) {
604 snprintf (buf, sizeof (buf), "-%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 "%c%02" PRIu32,
605 TC.hours, TC.minutes, TC.seconds, TC.drop ? ';' : ':', TC.frames);
606 } else {
607 snprintf (buf, sizeof (buf), " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 "%c%02" PRIu32,
608 TC.hours, TC.minutes, TC.seconds, TC.drop ? ';' : ':', TC.frames);
609 }
610 return std::string(buf);
611 }
612
timecode_format_sampletime(int64_t sample,double sample_sample_rate,double timecode_frames_per_second,bool timecode_drop_frames)613 std::string timecode_format_sampletime (
614 int64_t sample,
615 double sample_sample_rate,
616 double timecode_frames_per_second, bool timecode_drop_frames)
617 {
618 Time t;
619 sample_to_timecode(
620 sample, t, false, false,
621 timecode_frames_per_second, timecode_drop_frames,
622 sample_sample_rate,
623 80, false, 0);
624 return timecode_format_time(t);
625 }
626
parse_timecode_format(std::string tc,Timecode::Time & TC)627 bool parse_timecode_format(std::string tc, Timecode::Time &TC) {
628 char negative[2];
629 char ignored[2];
630 TC.subframes = 0;
631 if (sscanf (tc.c_str(), "%[- ]%" PRId32 ":%" PRId32 ":%" PRId32 "%[:;]%" PRId32,
632 negative, &TC.hours, &TC.minutes, &TC.seconds, ignored, &TC.frames) != 6) {
633 TC.hours = TC.minutes = TC.seconds = TC.frames = 0;
634 TC.negative = false;
635 return false;
636 }
637 if (negative[0]=='-') {
638 TC.negative = true;
639 } else {
640 TC.negative = false;
641 }
642 return true;
643 }
644
645 void
timecode_to_sample(Timecode::Time const & timecode,int64_t & sample,bool use_offset,bool use_subframes,double sample_sample_rate,uint32_t subframes_per_frame,bool offset_is_negative,int64_t offset_samples)646 timecode_to_sample(
647 Timecode::Time const& timecode, int64_t& sample,
648 bool use_offset, bool use_subframes,
649 /* Note - framerate info is taken from Timecode::Time& */
650 double sample_sample_rate /**< may include pull up/down */,
651 uint32_t subframes_per_frame,
652 /* optional offset - can be improved: function pointer to lazily query this*/
653 bool offset_is_negative, int64_t offset_samples
654 )
655 {
656 const double samples_per_timecode_frame = (double) sample_sample_rate / (double) timecode.rate;
657
658 if (timecode.drop) {
659 // The drop frame format was created to better approximate the 30000/1001 = 29.97002997002997....
660 // framerate of NTSC color TV. The used frame rate of drop fra,e is 29.97, which drifts by about
661 // 0.108 frame per hour, or about 1.3 frames per 12 hours. This is not perfect, but a lot better
662 // than using 30 non drop, which will drift with about 1.8 frame per minute.
663 // Using 29.97, drop frame real time can be accurate only every 10th minute (10 minutes of 29.97 fps
664 // is exactly 17982 samples). One minute is 1798.2 samples, but we count 30 frames per second
665 // (30 * 60 = 1800). This means that at the first minute boundary (at the end of 0:0:59:29) we
666 // are 1.8 framess too late relative to real time. By dropping 2 frames (jumping to 0:1:0:2) we are
667 // approx. 0.2 frames too early. This adds up with 0.2 too early for each minute until we are 1.8
668 // samples too early at 0:9:0:2 (9 * 0.2 = 1.8). The 10th minute brings us 1.8 frames later again
669 // (at end of 0:9:59:29), which sums up to 0 (we are back to zero at 0:10:0:0 :-).
670 //
671 // In table form:
672 //
673 // Timecode value frames offset subframes offset seconds (rounded) 44100 sample (rounded)
674 // 0:00:00:00 0.0 0 0.000 0 (accurate)
675 // 0:00:59:29 1.8 144 60.027 2647177
676 // 0:01:00:02 -0.2 -16 60.060 2648648
677 // 0:01:59:29 1.6 128 120.020 5292883
678 // 0:02:00:02 -0.4 -32 120.053 5294354
679 // 0:02:59:29 1.4 112 180.013 7938588
680 // 0:03:00:02 -0.6 -48 180.047 7940060
681 // 0:03:59:29 1.2 96 240.007 10584294
682 // 0:04:00:02 -0.8 -64 240.040 10585766
683 // 0:04:59:29 1.0 80 300.000 13230000
684 // 0:05:00:02 -1.0 -80 300.033 13231471
685 // 0:05:59:29 0.8 64 359.993 15875706
686 // 0:06:00:02 -1.2 -96 360.027 15877177
687 // 0:06:59:29 0.6 48 419.987 18521411
688 // 0:07:00:02 -1.4 -112 420.020 18522883
689 // 0:07:59:29 0.4 32 478.980 21167117
690 // 0:08:00:02 -1.6 -128 480.013 21168589
691 // 0:08:59:29 0.2 16 539.973 23812823
692 // 0:09:00:02 -1.8 -144 540.007 23814294
693 // 0:09:59:29 0.0+ 0+ 599.967 26458529
694 // 0:10:00:00 0.0 0 600.000 26460000 (accurate)
695 //
696 // Per Sigmond <per@sigmond.no>
697 //
698 // This schma would compensate exactly for a frame-rate of 30 * 0.999. but the
699 // actual rate is 30000/1001 - which results in an offset of -3.6ms per hour or
700 // about -86ms over a 24-hour period. (SMPTE 12M-1999)
701 //
702 // Robin Gareus <robin@gareus.org>
703
704 const int64_t fps_i = ceil(timecode.rate);
705 int64_t totalMinutes = 60 * timecode.hours + timecode.minutes;
706 int64_t frameNumber = fps_i * 3600 * timecode.hours
707 + fps_i * 60 * timecode.minutes
708 + fps_i * timecode.seconds + timecode.frames
709 - 2 * (totalMinutes - totalMinutes / 10);
710 sample = frameNumber * sample_sample_rate / (double) timecode.rate;
711 } else {
712 /*
713 Non drop is easy.. just note the use of
714 rint(timecode.rate) * samples_per_timecode_frame
715 (frames per Timecode second), which is larger than
716 sample_rate() in the non-integer Timecode rate case.
717 */
718
719 sample = (int64_t) rint(
720 (
721 ((timecode.hours * 60 * 60) + (timecode.minutes * 60) + timecode.seconds)
722 *
723 (rint(timecode.rate) * samples_per_timecode_frame)
724 )
725 + (timecode.frames * samples_per_timecode_frame)
726 );
727 }
728
729 if (use_subframes) {
730 sample += (int64_t) rint(((double)timecode.subframes * samples_per_timecode_frame) / (double)subframes_per_frame);
731 }
732
733 if (use_offset) {
734 if (offset_is_negative) {
735 if (sample >= offset_samples) {
736 sample -= offset_samples;
737 } else {
738 /* Prevent song-time from becoming negative */
739 sample = 0;
740 }
741 } else {
742 if (timecode.negative) {
743 if (sample <= offset_samples) {
744 sample = offset_samples - sample;
745 } else {
746 sample = 0;
747 }
748 } else {
749 sample += offset_samples;
750 }
751 }
752 }
753 }
754
755
756 void
sample_to_timecode(int64_t sample,Timecode::Time & timecode,bool use_offset,bool use_subframes,double timecode_frames_per_second,bool timecode_drop_frames,double sample_sample_rate,uint32_t subframes_per_frame,bool offset_is_negative,int64_t offset_samples)757 sample_to_timecode (
758 int64_t sample, Timecode::Time& timecode,
759 bool use_offset, bool use_subframes,
760 /* framerate info */
761 double timecode_frames_per_second,
762 bool timecode_drop_frames,
763 double sample_sample_rate/**< can include pull up/down */,
764 uint32_t subframes_per_frame,
765 /* optional offset - can be improved: function pointer to lazily query this*/
766 bool offset_is_negative, int64_t offset_samples
767 )
768 {
769 int64_t offset_sample;
770
771 if (!use_offset) {
772 timecode.negative = (sample < 0);
773 offset_sample = ::llabs(sample);
774 } else {
775 if (offset_is_negative) {
776 offset_sample = sample + offset_samples;
777 timecode.negative = false;
778 } else {
779 if (sample < offset_samples) {
780 offset_sample = (offset_samples - sample);
781 timecode.negative = true;
782 } else {
783 offset_sample = sample - offset_samples;
784 timecode.negative = false;
785 }
786 }
787 }
788
789 if (timecode_drop_frames) {
790 int64_t frameNumber = floor( (double)offset_sample * timecode_frames_per_second / sample_sample_rate);
791
792 /* there are 17982 samples in 10 min @ 29.97df */
793 const int64_t D = frameNumber / 17982;
794 const int64_t M = frameNumber % 17982;
795
796 timecode.subframes = rint(subframes_per_frame
797 * ((double)offset_sample * timecode_frames_per_second / sample_sample_rate - (double)frameNumber));
798
799 if (timecode.subframes == subframes_per_frame) {
800 timecode.subframes = 0;
801 frameNumber++;
802 }
803
804 frameNumber += 18*D + 2*((M - 2) / 1798);
805
806 timecode.frames = frameNumber % 30;
807 timecode.seconds = (frameNumber / 30) % 60;
808 timecode.minutes = ((frameNumber / 30) / 60) % 60;
809 timecode.hours = (((frameNumber / 30) / 60) / 60);
810
811 } else {
812 double timecode_frames_left_exact;
813 double timecode_frames_fraction;
814 int64_t timecode_frames_left;
815 const double samples_per_timecode_frame = sample_sample_rate / timecode_frames_per_second;
816 const int64_t frames_per_hour = (int64_t)(3600. * rint(timecode_frames_per_second) * samples_per_timecode_frame);
817
818 timecode.hours = offset_sample / frames_per_hour;
819
820 // Extract whole hours. Do this to prevent rounding errors with
821 // high sample numbers in the calculations that follow.
822 timecode_frames_left_exact = (double)(offset_sample % frames_per_hour) / samples_per_timecode_frame;
823 timecode_frames_fraction = timecode_frames_left_exact - floor( timecode_frames_left_exact );
824
825 timecode.subframes = (int32_t) rint(timecode_frames_fraction * subframes_per_frame);
826 timecode_frames_left = (int64_t) floor (timecode_frames_left_exact);
827
828 if (use_subframes && timecode.subframes == subframes_per_frame) {
829 timecode_frames_left++;
830 timecode.subframes = 0;
831 }
832
833 timecode.minutes = timecode_frames_left / ((int32_t) lrint (timecode_frames_per_second) * 60);
834 timecode_frames_left = timecode_frames_left % ((int32_t) lrint (timecode_frames_per_second) * 60);
835 timecode.seconds = timecode_frames_left / (int32_t) lrint(timecode_frames_per_second);
836 timecode.frames = timecode_frames_left % (int32_t) lrint(timecode_frames_per_second);
837 }
838
839 if (!use_subframes) {
840 timecode.subframes = 0;
841 }
842 /* set frame rate and drop sample */
843 timecode.rate = timecode_frames_per_second;
844 timecode.drop = timecode_drop_frames;
845 }
846
847 } // namespace Timecode
848
849 std::ostream&
operator <<(std::ostream & ostr,const Timecode::Time & t)850 operator<<(std::ostream& ostr, const Timecode::Time& t)
851 {
852 return t.print (ostr);
853 }
854