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