Merge branch 'master' into cairocanvas
[ardour.git] / libs / libltc / timecode.c
1 /*
2    libltc - en+decode linear timecode
3
4    Copyright (C) 2006-2012 Robin Gareus <robin@gareus.org>
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation, either version 3 of the
9    License, or (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 Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library.
18    If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "ltc/ltc.h"
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 /**
32  * SMPTE Timezones
33  */
34 struct SMPTETimeZonesStruct {
35         unsigned char code; //actually 6 bit!
36         char timezone[6];
37 };
38
39 /**
40  * SMPTE Timezone codes as per http://www.barney-wol.net/time/timecode.html
41  */
42 static const struct SMPTETimeZonesStruct smpte_time_zones[] =
43 {
44     /*  code,   timezone (UTC+)     //Standard time                 //Daylight saving   */
45     {   0x00,   "+0000"             /* Greenwich */                 /* - */             },
46     {   0x00,   "-0000"             /* Greenwich */                 /* - */             },
47     {   0x01,   "-0100"             /* Azores */                    /* - */             },
48     {   0x02,   "-0200"             /* Mid-Atlantic */              /* - */             },
49     {   0x03,   "-0300"             /* Buenos Aires */              /* Halifax */       },
50     {   0x04,   "-0400"             /* Halifax */                   /* New York */      },
51     {   0x05,   "-0500"             /* New York */                  /* Chicago */       },
52     {   0x06,   "-0600"             /* Chicago Denver */            /* - */             },
53     {   0x07,   "-0700"             /* Denver */                    /* Los Angeles */   },
54     {   0x08,   "-0800"             /* Los Angeles */               /* - */             },
55     {   0x09,   "-0900"             /* Alaska */                    /* - */             },
56     {   0x10,   "-1000"             /* Hawaii */                    /* - */             },
57     {   0x11,   "-1100"             /* Midway Island */             /* - */             },
58     {   0x12,   "-1200"             /* Kwaialein */                 /* - */             },
59     {   0x13,   "+1300"             /* - */                         /* New Zealand */   },
60     {   0x14,   "+1200"             /* New Zealand */               /* - */             },
61     {   0x15,   "+1100"             /* Solomon Islands */           /* - */             },
62     {   0x16,   "+1000"             /* Guam */                      /* - */             },
63     {   0x17,   "+0900"             /* Tokyo */                     /* - */             },
64     {   0x18,   "+0800"             /* Beijing */                   /* - */             },
65     {   0x19,   "+0700"             /* Bangkok */                   /* - */             },
66     {   0x20,   "+0600"             /* Dhaka */                     /* - */             },
67     {   0x21,   "+0500"             /* Islamabad */                 /* - */             },
68     {   0x22,   "+0400"             /* Abu Dhabi */                 /* - */             },
69     {   0x23,   "+0300"             /* Moscow */                    /* - */             },
70     {   0x24,   "+0200"             /* Eastern Europe */            /* - */             },
71     {   0x25,   "+0100"             /* Central Europe */            /* - */             },
72 /*  {   0x26,   "Undefined"         Reserved; do not use                                },*/
73 /*  {   0x27,   "Undefined"         Reserved; do not use                                },*/
74     {   0x28,   "TP-03"             /* Time precision class 3 */    /* - */             },
75     {   0x29,   "TP-02"             /* Time precision class 2 */    /* - */             },
76     {   0x30,   "TP-01"             /* Time precision class 1 */    /* - */             },
77     {   0x31,   "TP-00"             /* Time precision class 0 */    /* - */             },
78     {   0x0A,   "-0030"             /* - */                         /* - */             },
79     {   0x0B,   "-0130"             /* - */                         /* - */             },
80     {   0x0C,   "-0230"             /* - */                         /* Newfoundland */  },
81     {   0x0D,   "-0330"             /* Newfoundland */              /* - */             },
82     {   0x0E,   "-0430"             /* - */                         /* - */             },
83     {   0x0F,   "-0530"             /* - */                         /* - */             },
84     {   0x1A,   "-0630"             /* - */                         /* - */             },
85     {   0x1B,   "-0730"             /* - */                         /* - */             },
86     {   0x1C,   "-0830"             /* - */                         /* - */             },
87     {   0x1D,   "-0930"             /* Marquesa Islands */          /* - */             },
88     {   0x1E,   "-1030"             /* - */                         /* - */             },
89     {   0x1F,   "-1130"             /* - */                         /* - */             },
90     {   0x2A,   "+1130"             /* Norfolk Island */            /* - */             },
91     {   0x2B,   "+1030"             /* Lord Howe Is. */             /* - */             },
92     {   0x2C,   "+0930"             /* Darwin */                    /* - */             },
93     {   0x2D,   "+0830"             /* - */                         /* - */             },
94     {   0x2E,   "+0730"             /* - */                         /* - */             },
95     {   0x2F,   "+0630"             /* Rangoon */                   /* - */             },
96     {   0x3A,   "+0530"             /* Bombay */                    /* - */             },
97     {   0x3B,   "+0430"             /* Kabul */                     /* - */             },
98     {   0x3C,   "+0330"             /* Tehran */                    /* - */             },
99     {   0x3D,   "+0230"             /* - */                         /* - */             },
100     {   0x3E,   "+0130"             /* - */                         /* - */             },
101     {   0x3F,   "+0030"             /* - */                         /* - */             },
102     {   0x32,   "+1245"             /* Chatham Island */            /* - */             },
103 /*  {   0x33,   "Undefined"         Reserved; do not use                                },*/
104 /*  {   0x34,   "Undefined"         Reserved; do not use                                },*/
105 /*  {   0x35,   "Undefined"         Reserved; do not use                                },*/
106 /*  {   0x36,   "Undefined"         Reserved; do not use                                },*/
107 /*  {   0x37,   "Undefined"         Reserved; do not use                                },*/
108     {   0x38,   "+XXXX"             /* User defined time offset */  /* - */             },
109 /*  {   0x39,   "Undefined"         Unknown                         Unknown             },*/
110 /*  {   0x39,   "Undefined"         Unknown                         Unknown             },*/
111
112     {   0xFF,   ""                  /* The End */                                       }
113 };
114
115 static void smpte_set_timezone_string(LTCFrame *frame, SMPTETimecode *stime) {
116         int i = 0;
117
118         const unsigned char code = frame->user7 + (frame->user8 << 4);
119
120         char timezone[6] = "+0000";
121
122         for (i = 0 ; smpte_time_zones[i].code != 0xFF ; i++) {
123                 if ( smpte_time_zones[i].code == code ) {
124                         strcpy(timezone, smpte_time_zones[i].timezone);
125                         break;
126                 }
127         }
128         strcpy(stime->timezone, timezone);
129 }
130
131 static void smpte_set_timezone_code(SMPTETimecode *stime, LTCFrame *frame) {
132         int i = 0;
133         unsigned char code = 0x00;
134
135         // Find code for timezone string
136         // Primitive search
137         for (i=0; smpte_time_zones[i].code != 0xFF; i++) {
138                 if ( (strcmp(smpte_time_zones[i].timezone, stime->timezone)) == 0 ) {
139                         code = smpte_time_zones[i].code;
140                         break;
141                 }
142         }
143
144         frame->user7 = code & 0x0F;
145         frame->user8 = (code & 0xF0) >> 4;
146 }
147
148 /** Drop-frame support function
149  * We skip the first two frame numbers (0 and 1) at the beginning of each minute,
150  * except for minutes 0, 10, 20, 30, 40, and 50
151  * (i.e. we skip frame numbers at the beginning of minutes for which mins_units is not 0).
152  */
153 static void skip_drop_frames(LTCFrame* frame) {
154         if ((frame->mins_units != 0)
155                 && (frame->secs_units == 0)
156                 && (frame->secs_tens == 0)
157                 && (frame->frame_units == 0)
158                 && (frame->frame_tens == 0)
159                 ) {
160                 frame->frame_units += 2;
161         }
162 }
163
164 void ltc_frame_to_time(SMPTETimecode *stime, LTCFrame *frame, int flags) {
165         if (!stime) return;
166
167         if (flags & LTC_USE_DATE) {
168                 smpte_set_timezone_string(frame, stime);
169
170                 stime->years  = frame->user5 + frame->user6*10;
171                 stime->months = frame->user3 + frame->user4*10;
172                 stime->days   = frame->user1 + frame->user2*10;
173         } else {
174                 stime->years  = 0;
175                 stime->months = 0;
176                 stime->days   = 0;
177                 sprintf(stime->timezone,"+0000");
178         }
179
180         stime->hours = frame->hours_units + frame->hours_tens*10;
181         stime->mins  = frame->mins_units  + frame->mins_tens*10;
182         stime->secs  = frame->secs_units  + frame->secs_tens*10;
183         stime->frame = frame->frame_units + frame->frame_tens*10;
184 }
185
186 void ltc_time_to_frame(LTCFrame* frame, SMPTETimecode* stime, enum LTC_TV_STANDARD standard, int flags) {
187         if (flags & LTC_USE_DATE) {
188                 smpte_set_timezone_code(stime, frame);
189                 frame->user6 = stime->years/10;
190                 frame->user5 = stime->years - frame->user6*10;
191                 frame->user4 = stime->months/10;
192                 frame->user3 = stime->months - frame->user4*10;
193                 frame->user2 = stime->days/10;
194                 frame->user1 = stime->days - frame->user2*10;
195         }
196
197         frame->hours_tens  = stime->hours/10;
198         frame->hours_units = stime->hours - frame->hours_tens*10;
199         frame->mins_tens   = stime->mins/10;
200         frame->mins_units  = stime->mins - frame->mins_tens*10;
201         frame->secs_tens   = stime->secs/10;
202         frame->secs_units  = stime->secs - frame->secs_tens*10;
203         frame->frame_tens  = stime->frame/10;
204         frame->frame_units = stime->frame - frame->frame_tens*10;
205
206         // Prevent illegal SMPTE frames
207         if (frame->dfbit) {
208                 skip_drop_frames(frame);
209         }
210
211         if ((flags & LTC_NO_PARITY) == 0) {
212                 ltc_frame_set_parity(frame, standard);
213         }
214 }
215
216 void ltc_frame_reset(LTCFrame* frame) {
217         memset(frame, 0, sizeof(LTCFrame));
218         // syncword = 0x3FFD
219 #ifdef __BIG_ENDIAN__
220         // mirrored BE bit order: FCBF
221         frame->sync_word = 0xFCBF;
222 #else
223         // mirrored LE bit order: BFFC
224         frame->sync_word = 0xBFFC;
225 #endif
226 }
227
228 int ltc_frame_increment(LTCFrame* frame, int fps, enum LTC_TV_STANDARD standard, int flags) {
229         int rv = 0;
230
231         frame->frame_units++;
232
233         if (frame->frame_units == 10)
234         {
235                 frame->frame_units = 0;
236                 frame->frame_tens++;
237         }
238         if (fps == frame->frame_units+frame->frame_tens*10)
239         {
240                 frame->frame_units = 0;
241                 frame->frame_tens = 0;
242                 frame->secs_units++;
243                 if (frame->secs_units == 10)
244                 {
245                         frame->secs_units = 0;
246                         frame->secs_tens++;
247                         if (frame->secs_tens == 6)
248                         {
249                                 frame->secs_tens = 0;
250                                 frame->mins_units++;
251                                 if (frame->mins_units == 10)
252                                 {
253                                         frame->mins_units = 0;
254                                         frame->mins_tens++;
255                                         if (frame->mins_tens == 6)
256                                         {
257                                                 frame->mins_tens = 0;
258                                                 frame->hours_units++;
259                                                 if (frame->hours_units == 10)
260                                                 {
261                                                         frame->hours_units = 0;
262                                                         frame->hours_tens++;
263                                                 }
264                                                 if (frame->hours_units == 4 && frame->hours_tens==2)
265                                                 {
266                                                         /* 24h wrap around */
267                                                         rv=1;
268                                                         frame->hours_tens=0;
269                                                         frame->hours_units = 0;
270
271                                                         if (flags&1)
272                                                         {
273                                                                 /* wrap date */
274                                                                 SMPTETimecode stime;
275                                                                 stime.years  = frame->user5 + frame->user6*10;
276                                                                 stime.months = frame->user3 + frame->user4*10;
277                                                                 stime.days   = frame->user1 + frame->user2*10;
278
279                                                                 if (stime.months > 0 && stime.months < 13)
280                                                                 {
281                                                                         unsigned char dpm[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
282                                                                         /* proper leap-year calc:
283                                                                          * ((stime.years%4)==0 && ( (stime.years%100) != 0 || (stime.years%400) == 0) )
284                                                                          * simplified since year is 0..99
285                                                                          */
286                                                                         if ((stime.years%4)==0 /* && stime.years!=0 */ ) /* year 2000 was a leap-year */
287                                                                                 dpm[1]=29;
288                                                                         stime.days++;
289                                                                         if (stime.days > dpm[stime.months-1])
290                                                                         {
291                                                                                 stime.days=1;
292                                                                                 stime.months++;
293                                                                                 if (stime.months > 12) {
294                                                                                         stime.months=1;
295                                                                                         stime.years=(stime.years+1)%100;
296                                                                                 }
297                                                                         }
298                                                                         frame->user6 = stime.years/10;
299                                                                         frame->user5 = stime.years%10;
300                                                                         frame->user4 = stime.months/10;
301                                                                         frame->user3 = stime.months%10;
302                                                                         frame->user2 = stime.days/10;
303                                                                         frame->user1 = stime.days%10;
304                                                                 } else {
305                                                                         rv=-1;
306                                                                 }
307                                                         }
308                                                 }
309                                         }
310                                 }
311                         }
312                 }
313         }
314
315         if (frame->dfbit) {
316                 skip_drop_frames(frame);
317         }
318
319         if ((flags & LTC_NO_PARITY) == 0) {
320                 ltc_frame_set_parity(frame, standard);
321         }
322
323         return rv;
324 }
325
326 int ltc_frame_decrement(LTCFrame* frame, int fps, enum LTC_TV_STANDARD standard, int flags) {
327         int rv = 0;
328
329         int frames = frame->frame_units + frame->frame_tens * 10;
330         if (frames > 0) {
331                 frames--;
332         } else {
333                 frames = fps -1;
334         }
335
336         frame->frame_units = frames % 10;
337         frame->frame_tens  = frames / 10;
338
339         if (frames == fps -1) {
340                 int secs = frame->secs_units + frame->secs_tens * 10;
341                 if (secs > 0) {
342                         secs--;
343                 } else {
344                         secs = 59;
345                 }
346                 frame->secs_units = secs % 10;
347                 frame->secs_tens  = secs / 10;
348
349                 if (secs == 59) {
350                         int mins = frame->mins_units + frame->mins_tens * 10;
351                         if (mins > 0) {
352                                 mins--;
353                         } else {
354                                 mins = 59;
355                         }
356                         frame->mins_units = mins % 10;
357                         frame->mins_tens  = mins / 10;
358
359                         if (mins == 59) {
360                                 int hours = frame->hours_units + frame->hours_tens * 10;
361                                 if (hours > 0) {
362                                         hours--;
363                                 } else {
364                                         hours = 23;
365                                 }
366                                 frame->hours_units = hours % 10;
367                                 frame->hours_tens  = hours / 10;
368
369                                 if (hours == 23) {
370                                         /* 24h wrap around */
371                                         rv=1;
372                                         if (flags&LTC_USE_DATE)
373                                         {
374                                                 /* wrap date */
375                                                 SMPTETimecode stime;
376                                                 stime.years  = frame->user5 + frame->user6*10;
377                                                 stime.months = frame->user3 + frame->user4*10;
378                                                 stime.days   = frame->user1 + frame->user2*10;
379
380                                                 if (stime.months > 0 && stime.months < 13)
381                                                 {
382                                                         unsigned char dpm[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
383                                                         /* proper leap-year calc:
384                                                          * ((stime.years%4)==0 && ( (stime.years%100) != 0 || (stime.years%400) == 0) )
385                                                          * simplified since year is 0..99
386                                                          */
387                                                         if ((stime.years%4)==0 /* && stime.years!=0 */ ) /* year 2000 was a leap-year */
388                                                                 dpm[1]=29;
389                                                         //
390                                                         if (stime.days > 1) {
391                                                                 stime.days--;
392                                                         } else {
393                                                                 stime.months = 1 + (stime.months + 10)%12;
394                                                                 stime.days = dpm[stime.months-1];
395                                                                 if (stime.months == 12)  {
396                                                                         stime.years=(stime.years+99)%100; // XXX
397                                                                 }
398                                                         }
399
400                                                         frame->user6 = stime.years/10;
401                                                         frame->user5 = stime.years%10;
402                                                         frame->user4 = stime.months/10;
403                                                         frame->user3 = stime.months%10;
404                                                         frame->user2 = stime.days/10;
405                                                         frame->user1 = stime.days%10;
406                                                 } else {
407                                                         rv=-1;
408                                                 }
409                                         }
410                                 }
411                         }
412                 }
413         }
414
415         if (frame->dfbit && /* prevent endless recursion */ fps > 2) {
416                 if ((frame->mins_units != 0)
417                         && (frame->secs_units == 0)
418                         && (frame->secs_tens == 0)
419                         && (frame->frame_units == 1)
420                         && (frame->frame_tens == 0)
421                         ) {
422                         ltc_frame_decrement(frame, fps, standard, flags&LTC_USE_DATE);
423                         ltc_frame_decrement(frame, fps, standard, flags&LTC_USE_DATE);
424                 }
425         }
426
427         if ((flags & LTC_NO_PARITY) == 0) {
428                 ltc_frame_set_parity(frame, standard);
429         }
430
431         return rv;
432 }
433
434 int parse_bcg_flags(LTCFrame *f, enum LTC_TV_STANDARD standard) {
435         switch (standard) {
436                 case LTC_TV_625_50: /* 25 fps mode */
437                         return (
438                                           ((f->binary_group_flag_bit0)?4:0)
439                                         | ((f->binary_group_flag_bit1)?2:0)
440                                         | ((f->biphase_mark_phase_correction)?1:0)
441                                         );
442                         break;
443                 default: /* 24,30 fps mode */
444                         return (
445                                           ((f->binary_group_flag_bit2)?4:0)
446                                         | ((f->binary_group_flag_bit1)?2:0)
447                                         | ((f->binary_group_flag_bit0)?1:0)
448                                         );
449                         break;
450         }
451 }