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