1/***********************************************************************
2 * AUTHOR: David McPaul
3 *   FILE: TimeCode.cpp
4 *  DESCR: Handles all TimeCode functions.
5 ***********************************************************************/
6#include <TimeCode.h>
7#include "MediaDebug.h"
8#include <string.h>
9
10status_t us_to_timecode(bigtime_t micros, int * hours, int * minutes, int * seconds, int * frames, const timecode_info * code)
11{
12	int32 l_frames;
13
14	CALLED();
15
16	if (code) {
17		// We have a valid timecode_info
18		switch (code->type) {
19			case	B_TIMECODE_DEFAULT:		// NTSC
20			case	B_TIMECODE_30_DROP_2:	// NTSC
21				l_frames = int32((((micros % 1000000) * 29.97) / 1000000) + (micros / 1000000 * 29.97));
22				break;
23			case	B_TIMECODE_30_DROP_4:	// Brazil
24				l_frames = int32((((micros % 1000000) * 29.95) / 1000000) + (micros / 1000000 * 29.95));
25				break;
26			default:
27				l_frames = (((micros % 1000000) * code->fps_div) / 1000000) + (micros / 1000000 * code->fps_div);
28				break;
29		};
30	} else {
31		// Convert us to frames
32		l_frames = int32((((micros % 1000000) * 29.97) / 1000000) + (micros / 1000000 * 29.97));
33	}
34
35	return frames_to_timecode(l_frames, hours, minutes, seconds, frames, code);
36}
37
38status_t timecode_to_us(int hours, int minutes, int seconds, int frames, bigtime_t * micros, const timecode_info * code)
39{
40	int32 l_frames;
41
42	CALLED();
43
44	if (timecode_to_frames(hours, minutes, seconds, frames, &l_frames, code) == B_OK) {
45
46		if (code) {
47			// We have a valid timecode_info
48			switch (code->type) {
49				case	B_TIMECODE_DEFAULT:		// NTSC
50				case	B_TIMECODE_30_DROP_2:	// NTSC
51					*micros = bigtime_t(l_frames * ((1000000 / 29.97) + 0.5));
52					break;
53				case	B_TIMECODE_30_DROP_4:	// Brazil
54					*micros = bigtime_t(l_frames * ((1000000 / 29.95) + 0.5));
55					break;
56				default:
57					*micros = l_frames * 1000000 / code->fps_div;
58					break;
59			};
60
61		} else {
62			*micros = bigtime_t(l_frames * ((1000000 / 29.97) + 0.5));
63		}
64
65		return B_OK;
66	}
67	return B_ERROR;
68}
69
70status_t frames_to_timecode(int32 l_frames, int * hours, int * minutes, int * seconds, int * frames, const timecode_info * code)
71{
72	int fps_div;
73	int total_mins;
74	int32 extra_frames;
75	int32 extra_frames2;
76
77	CALLED();
78
79	if (code) {
80		// We have a valid timecode_info so use it
81		fps_div = code->fps_div;
82
83		if (code->every_nth > 0) {
84			// Handle Drop Frames format
85
86			total_mins = l_frames / fps_div / 60;
87
88			// "every_nth" minute we gain "drop_frames" "except_nth" minute
89			extra_frames = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth);
90			l_frames += extra_frames;
91
92			total_mins = l_frames / fps_div / 60;
93			extra_frames2 = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth);
94
95			// Gaining frames may mean that we gain more frames so we keep adjusting until no more to adjust
96			while (extra_frames != extra_frames2) {
97				l_frames += extra_frames2 - extra_frames;
98				extra_frames = extra_frames2;
99
100				total_mins = l_frames / fps_div / 60;
101				extra_frames2 = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth);
102			}
103
104			// l_frames should now include all adjusted frames
105		}
106	} else {
107		// no timecode_info so set default NTSC :-(
108		fps_div = 30;		// NTSC Default
109		total_mins = l_frames / fps_div / 60;
110
111		// "every_nth" minute we gain "drop_frames" "except_nth" minute
112		extra_frames = 2*(total_mins) - 2*(total_mins/10);
113		l_frames += extra_frames;
114
115		total_mins = l_frames / fps_div / 60;
116		extra_frames2 = 2*(total_mins) - 2*(total_mins/10);
117
118		// Gaining frames may mean that we gain more frames so we keep adjusting until no more to adjust
119		while (extra_frames != extra_frames2) {
120			l_frames += extra_frames2 - extra_frames;
121			extra_frames = extra_frames2;
122
123			total_mins = l_frames / fps_div / 60;
124			extra_frames2 = 2*(total_mins) - 2*(total_mins/10);
125		}
126	}
127
128	// convert frames to seconds leaving the remainder as frames
129	*seconds = l_frames / fps_div;		// DIV
130	*frames = l_frames % fps_div;		// MOD
131
132	// Normalise to Hours Minutes Seconds Frames
133	*minutes = *seconds / 60;
134	*seconds = *seconds % 60;
135
136	*hours = *minutes / 60;
137	*minutes = *minutes % 60;
138
139	return B_OK;
140}
141
142status_t timecode_to_frames(int hours, int minutes, int seconds, int frames, int32 * l_frames, const timecode_info * code)
143{
144	int fps_div;
145	int total_mins;
146	int32 extra_frames;
147
148	CALLED();
149
150	if (code) {
151		// We have a valid timecode_info
152		fps_div = code->fps_div;
153
154		total_mins = (hours * 60) + minutes;
155		*l_frames = (total_mins * 60) + seconds;
156		*l_frames = (*l_frames * fps_div) + frames;
157
158		// Adjust "every_nth" minute we lose "drop_frames" "except_nth" minute
159		if (code->every_nth > 0) {
160			extra_frames = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth);
161
162			*l_frames = *l_frames - extra_frames;
163		}
164	} else {
165
166		total_mins = (hours * 60) + minutes;
167		*l_frames = (total_mins * 60) + seconds;
168		*l_frames = (*l_frames * 30) + frames;
169
170		// Adjust "every_nth" minute we lose "drop_frames" "except_nth" minute
171		extra_frames = 2*(total_mins) - 2*(total_mins/10);
172
173		*l_frames = *l_frames - extra_frames;
174	}
175
176	return B_OK;
177}
178
179status_t get_timecode_description(timecode_type type, timecode_info * out_timecode)
180{
181	CALLED();
182
183	out_timecode->type = type;
184	strncpy(out_timecode->format,"%.2ih:%.2im:%.2is.%.2i",31);
185	out_timecode->every_nth = 0;
186	out_timecode->except_nth = 0;
187
188	switch (type) {
189		case	B_TIMECODE_100:
190				strncpy(out_timecode->name,"100 FPS",31);
191				out_timecode->fps_div = 100;
192				break;
193		case	B_TIMECODE_75:			// CD
194				strncpy(out_timecode->name,"CD",31);
195				out_timecode->fps_div = 75;
196				break;
197		case	B_TIMECODE_30:			// MIDI
198				strncpy(out_timecode->name,"MIDI",31);
199				out_timecode->fps_div = 30;
200				break;
201		case	B_TIMECODE_30_DROP_2:	// NTSC
202				strncpy(out_timecode->name,"NTSC",31);
203				out_timecode->fps_div = 30;	// This is supposed to be 29.97fps but can be simulated using the drop frame format below
204				out_timecode->drop_frames = 2; // Drop 2 frames
205				out_timecode->every_nth = 1;	// every 1 minutes
206				out_timecode->except_nth = 10;	// except every 10 minutes
207				break;
208		case	B_TIMECODE_30_DROP_4:	// Brazil
209				strncpy(out_timecode->name,"NTSC Brazil",31);
210				out_timecode->fps_div = 30;
211				out_timecode->drop_frames = 4; // Drop 4 frames
212				out_timecode->every_nth = 1;	// every 1 minutes
213				out_timecode->except_nth = 10;	// except every 10 minutes
214				break;
215		case	B_TIMECODE_25:			// PAL
216				strncpy(out_timecode->name,"PAL",31);
217				out_timecode->fps_div = 25;
218				break;
219		case	B_TIMECODE_24:			// Film
220				strncpy(out_timecode->name,"FILM",31);
221				out_timecode->fps_div = 24;
222				break;
223		case	B_TIMECODE_18:			// Super8
224				strncpy(out_timecode->name,"Super 8",31);
225				out_timecode->fps_div = 18;
226				break;
227		default:
228				strncpy(out_timecode->name,"NTSC",31);
229				out_timecode->fps_div = 30;	// This is supposed to be 29.97fps but can be simulated using the drop frame format below
230				out_timecode->drop_frames = 2; // Drop 2 frames
231				out_timecode->every_nth = 1;	// every 1 minutes
232				out_timecode->except_nth = 10;	// except every 10 minutes
233				break;
234	}
235
236	return B_OK;
237}
238
239status_t count_timecodes()
240{
241	CALLED();
242	return 8;	// Is this right?
243}
244
245/*************************************************************
246 * public BTimeCode
247 *************************************************************/
248
249
250BTimeCode::BTimeCode()
251{
252	CALLED();
253}
254
255
256BTimeCode::BTimeCode(bigtime_t us,
257					 timecode_type type)
258{
259	CALLED();
260	if (SetType(type) == B_OK) {
261		SetMicroseconds(us);
262	}
263}
264
265
266BTimeCode::BTimeCode(const BTimeCode &clone)
267{
268	CALLED();
269	if (SetType(clone.Type()) == B_OK) {
270		SetData(clone.Hours(),clone.Minutes(),clone.Seconds(),clone.Frames());
271	}
272}
273
274
275BTimeCode::BTimeCode(int hours,
276					 int minutes,
277					 int seconds,
278					 int frames,
279					 timecode_type type)
280{
281	CALLED();
282	if (SetType(type) == B_OK) {
283		SetData(hours,minutes,seconds,frames);
284	}
285}
286
287
288BTimeCode::~BTimeCode()
289{
290	CALLED();
291}
292
293
294void
295BTimeCode::SetData(int hours,
296				   int minutes,
297				   int seconds,
298				   int frames)
299{
300	CALLED();
301	fHours = hours;
302	fMinutes = minutes;
303	fSeconds = seconds;
304	fFrames = frames;
305}
306
307
308status_t
309BTimeCode::SetType(timecode_type type)
310{
311	CALLED();
312
313	return get_timecode_description(type, &fInfo);
314}
315
316
317void
318BTimeCode::SetMicroseconds(bigtime_t us)
319{
320	CALLED();
321
322	us_to_timecode(us, &fHours, &fMinutes, &fSeconds, &fFrames, &fInfo);
323}
324
325
326void
327BTimeCode::SetLinearFrames(int32 linear_frames)
328{
329	CALLED();
330
331	frames_to_timecode(linear_frames, &fHours, &fMinutes, &fSeconds, &fFrames, &fInfo);
332}
333
334
335BTimeCode &
336BTimeCode::operator=(const BTimeCode &clone)
337{
338	CALLED();
339	fHours = clone.Hours();
340	fMinutes = clone.Minutes();
341	fSeconds = clone.Seconds();
342	fFrames = clone.Frames();
343	SetType(clone.Type());
344
345	return *this;
346}
347
348
349bool
350BTimeCode::operator==(const BTimeCode &other) const
351{
352	CALLED();
353
354	return ((this->LinearFrames()) == (other.LinearFrames()));
355}
356
357
358bool
359BTimeCode::operator<(const BTimeCode &other) const
360{
361	CALLED();
362
363	return ((this->LinearFrames()) < (other.LinearFrames()));
364}
365
366
367BTimeCode &
368BTimeCode::operator+=(const BTimeCode &other)
369{
370	CALLED();
371
372	this->SetLinearFrames(this->LinearFrames() + other.LinearFrames());
373
374	return *this;
375}
376
377
378BTimeCode &
379BTimeCode::operator-=(const BTimeCode &other)
380{
381	CALLED();
382
383	this->SetLinearFrames(this->LinearFrames() - other.LinearFrames());
384
385	return *this;
386}
387
388
389BTimeCode
390BTimeCode::operator+(const BTimeCode &other) const
391{
392	CALLED();
393	BTimeCode tc(*this);
394	tc += other;
395	return tc;
396}
397
398
399BTimeCode
400BTimeCode::operator-(const BTimeCode &other) const
401{
402	CALLED();
403	BTimeCode tc(*this);
404	tc -= other;
405	return tc;
406}
407
408
409int
410BTimeCode::Hours() const
411{
412	CALLED();
413
414	return fHours;
415}
416
417
418int
419BTimeCode::Minutes() const
420{
421	CALLED();
422
423	return fMinutes;
424}
425
426
427int
428BTimeCode::Seconds() const
429{
430	CALLED();
431
432	return fSeconds;
433}
434
435
436int
437BTimeCode::Frames() const
438{
439	CALLED();
440
441	return fFrames;
442}
443
444
445timecode_type
446BTimeCode::Type() const
447{
448	CALLED();
449
450	return fInfo.type;
451}
452
453
454void
455BTimeCode::GetData(int *out_hours,
456				   int *out_minutes,
457				   int *out_seconds,
458				   int *out_frames,
459				   timecode_type *out_type) const
460{
461	CALLED();
462	*out_hours = fHours;
463	*out_minutes = fMinutes;
464	*out_seconds = fSeconds;
465	*out_frames = fFrames;
466	*out_type = fInfo.type;
467}
468
469
470bigtime_t
471BTimeCode::Microseconds() const
472{
473	bigtime_t us;
474
475	CALLED();
476
477	if (timecode_to_us(fHours,fMinutes,fSeconds,fFrames, &us, &fInfo) == B_OK) {
478		return us;
479	}
480
481	return 0;
482}
483
484
485int32
486BTimeCode::LinearFrames() const
487{
488	int32 linear_frames;
489
490	CALLED();
491
492	if (timecode_to_frames(fHours,fMinutes,fSeconds,fFrames,&linear_frames,&fInfo) == B_OK) {
493		return linear_frames;
494	}
495
496	return 0;
497}
498
499
500void
501BTimeCode::GetString(char *str) const
502{
503	CALLED();
504	sprintf(str,fInfo.format, fHours, fMinutes, fSeconds, fFrames);
505}
506