]> git.sesse.net Git - kdenlive/blob - src/timecode.cpp
Fix timecode calculation
[kdenlive] / src / timecode.cpp
1 /***************************************************************************
2                           timecode  -  description
3                              -------------------
4     begin                : Wed Dec 17 2003
5     copyright            : (C) 2003 by Jason Wood
6     email                : jasonwood@blueyonder.co.uk
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 #include <QValidator>
18
19 #include <KDebug>
20 #include <KLocale>
21
22 #include "timecode.h"
23
24 Timecode::Timecode(Formats format, double framesPerSecond, bool dropFrame)
25 {
26     m_validator = new QRegExpValidator(0);
27     setFormat(framesPerSecond, dropFrame, format);
28 }
29
30 Timecode::~Timecode()
31 {
32 }
33
34 void Timecode::setFormat(double framesPerSecond, bool dropFrame, Formats format)
35 {
36     m_displayedFramesPerSecond = (int)(framesPerSecond + 0.5);
37     m_dropFrame = dropFrame;
38     m_format = format;
39     m_realFps = framesPerSecond;
40     m_dropFrames = round(m_realFps * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate
41     QRegExp regExp;
42     if (m_dropFrame)
43         regExp.setPattern("^\\d{2}:\\d{2}:\\d{2};\\d{2}$");
44     else
45         regExp.setPattern("^\\d{2}:\\d{2}:\\d{2}:\\d{2}$");
46     m_validator->setRegExp(regExp);
47 }
48
49 double Timecode::fps() const
50 {
51     return m_realFps; //m_displayedFramesPerSecond;
52 }
53
54 bool Timecode::df() const
55 {
56     return m_dropFrame;
57 }
58
59 const QValidator *Timecode::validator() const
60 {
61     return m_validator;
62 }
63
64 QString Timecode::reformatSeparators(QString duration) const
65 {
66     if (m_dropFrame)
67         return duration.replace(8, 1, ';');
68     return duration.replace(8, 1, ':');
69 }
70
71 int Timecode::getDisplayFrameCount(const QString duration, bool frameDisplay) const
72 {
73     if (frameDisplay) return duration.toInt();
74     return getFrameCount(duration);
75 }
76
77 int Timecode::getFrameCount(const QString duration) const
78 {
79     if (m_dropFrame) {
80
81         //CONVERT DROP FRAME TIMECODE TO A FRAME NUMBER
82         //Code by David Heidelberger, adapted from Andrew Duncan
83         //Given ints called hours, minutes, seconds, frames, and a double called framerate
84
85         //Get Hours, Minutes, Seconds, Frames from timecode
86         int hours, minutes, seconds, frames;
87
88         hours = duration.section(':', 0, 0).toInt();
89         minutes = duration.section(':', 1, 1).toInt();
90         if (duration.contains(';')) {
91             seconds = duration.section(';', 0, 0).section(':', 2, 2).toInt();
92             frames = duration.section(';', 1, 1).toInt();
93         } else {
94             //Handle Drop Frame timecode frame calculations, even if the timecode supplied uses incorrect "99:99:99:99" format instead of "99:99:99;99"
95             seconds = duration.section(':', 2, 2).toInt();
96             frames = duration.section(':', 3, 3).toInt();
97         }
98
99         int totalMinutes = (60 * hours) + minutes; //Total number of minutes
100         int frameNumber = ((m_displayedFramesPerSecond * 60 * 60 * hours) + (m_displayedFramesPerSecond * 60 * minutes) + (m_displayedFramesPerSecond * seconds) + frames) - (m_dropFrames * (totalMinutes - floor(totalMinutes / 10)));
101         return frameNumber;
102
103
104         //Calculate the frame count
105         int dropRate = (int)((ceil(m_displayedFramesPerSecond) / 30) * 2);
106         frames += ((hours * 60 + minutes) * 60 + seconds) * m_displayedFramesPerSecond;
107         frames -= dropRate * ((hours * 60 + minutes) - (floor((hours * 60 + minutes) / 10)));
108         return frames;
109     }
110     return (int)((duration.section(':', 0, 0).toInt()*3600.0 + duration.section(':', 1, 1).toInt()*60.0 + duration.section(':', 2, 2).toInt()) * m_realFps + duration.section(':', 3, 3).toInt());
111 }
112
113 QString Timecode::getDisplayTimecode(const GenTime & time, bool frameDisplay) const
114 {
115     if (frameDisplay) return QString::number((int) time.frames(m_realFps));
116     return getTimecode(time);
117 }
118
119 QString Timecode::getTimecode(const GenTime & time) const
120 {
121     switch (m_format) {
122     case HH_MM_SS_FF:
123         return getTimecodeHH_MM_SS_FF(time);
124         break;
125     case HH_MM_SS_HH:
126         return getTimecodeHH_MM_SS_HH(time);
127         break;
128     case Frames:
129         return getTimecodeFrames(time);
130         break;
131     case Seconds:
132         return getTimecodeSeconds(time);
133         break;
134     default:
135         kWarning() <<
136         "Unknown timecode format specified, defaulting to HH_MM_SS_FF"
137         << endl;
138         return getTimecodeHH_MM_SS_FF(time);
139     }
140 }
141
142 const QString Timecode::getDisplayTimecodeFromFrames(int frames, bool frameDisplay) const
143 {
144     if (frameDisplay) return QString::number(frames);
145     return getTimecodeHH_MM_SS_FF(frames);
146 }
147
148 const QString Timecode::getTimecodeFromFrames(int frames) const
149 {
150     return getTimecodeHH_MM_SS_FF(frames);
151 }
152
153
154 //static
155 QString Timecode::getStringTimecode(int frames, const double &fps)
156 {
157     // Returns the timecode in an hh:mm:ss format
158     int seconds = (int)(frames / fps);
159     int minutes = seconds / 60;
160     seconds = seconds % 60;
161     int hours = minutes / 60;
162     minutes = minutes % 60;
163     QString text;
164     text.append(QString::number(hours).rightJustified(2, '0', false));
165     text.append(':');
166     text.append(QString::number(minutes).rightJustified(2, '0', false));
167     text.append(':');
168     text.append(QString::number(seconds).rightJustified(2, '0', false));
169     return text;
170 }
171
172
173 //static
174 QString Timecode::getEasyTimecode(const GenTime & time, const double &fps)
175 {
176     // Returns the timecode in an easily read display, like 3 min. 5 sec.
177     int frames = (int) time.frames(fps);
178     int seconds = (int)(frames / fps);
179     frames = frames - ((int)(fps * seconds));
180
181     int minutes = seconds / 60;
182     seconds = seconds % 60;
183     int hours = minutes / 60;
184     minutes = minutes % 60;
185
186     QString text;
187     bool trim = false;
188
189     if (hours != 0) {
190         text.append(QString::number(hours).rightJustified(2, '0', false));
191         text.append(' ' + i18n("hour") + ' ');
192         trim = true;
193     }
194     if (minutes != 0 || trim) {
195         if (!trim) {
196             text.append(QString::number(minutes));
197         } else
198             text.append(QString::number(minutes).rightJustified(2, '0', false));
199         text.append(' ' + i18n("min.") + ' ');
200         trim = true;
201     }
202     if (seconds != 0 || trim) {
203         if (!trim) {
204             text.append(QString::number(seconds));
205         } else
206             text.append(QString::number(seconds).rightJustified(2, '0', false));
207         text.append(' ' + i18n("sec."));
208         trim = true;
209     }
210     if (!trim) {
211         text.append(QString::number(frames));
212         text.append(' ' + i18n("frames"));
213     }
214
215     return text;
216 }
217
218
219 const QString Timecode::getTimecodeHH_MM_SS_FF(const GenTime & time) const
220 {
221     if (m_dropFrame)
222         return getTimecodeDropFrame(time);
223
224     return getTimecodeHH_MM_SS_FF((int) time.frames(m_realFps));
225 }
226
227 const QString Timecode::getTimecodeHH_MM_SS_FF(int frames) const
228 {
229     if (m_dropFrame) {
230         return getTimecodeDropFrame(frames);
231     }
232     int seconds = frames / m_displayedFramesPerSecond;
233     frames = frames % m_displayedFramesPerSecond;
234
235     int minutes = seconds / 60;
236     seconds = seconds % 60;
237     int hours = minutes / 60;
238     minutes = minutes % 60;
239
240     QString text;
241     text.append(QString::number(hours).rightJustified(2, '0', false));
242     text.append(':');
243     text.append(QString::number(minutes).rightJustified(2, '0', false));
244     text.append(':');
245     text.append(QString::number(seconds).rightJustified(2, '0', false));
246     text.append(':');
247     text.append(QString::number(frames).rightJustified(2, '0', false));
248
249     return text;
250 }
251
252 QString Timecode::getTimecodeHH_MM_SS_HH(const GenTime & time) const
253 {
254     int hundredths = (int)(time.seconds() * 100);
255     int seconds = hundredths / 100;
256     hundredths = hundredths % 100;
257     int minutes = seconds / 60;
258     seconds = seconds % 60;
259     int hours = minutes / 60;
260     minutes = minutes % 60;
261
262     QString text;
263
264     text.append(QString::number(hours).rightJustified(2, '0', false));
265     text.append(':');
266     text.append(QString::number(minutes).rightJustified(2, '0', false));
267     text.append(':');
268     text.append(QString::number(seconds).rightJustified(2, '0', false));
269     if (m_dropFrame)
270         text.append(';');
271     else
272         text.append(':');
273     text.append(QString::number(hundredths).rightJustified(2, '0', false));
274
275     return text;
276 }
277
278 QString Timecode::getTimecodeFrames(const GenTime & time) const
279 {
280     return QString::number(time.frames(m_realFps));
281 }
282
283 QString Timecode::getTimecodeSeconds(const GenTime & time) const
284 {
285     return QString::number(time.seconds());
286 }
287
288 QString Timecode::getTimecodeDropFrame(const GenTime & time) const
289 {
290     return getTimecodeDropFrame((int)time.frames(m_realFps));
291 }
292
293 QString Timecode::getTimecodeDropFrame(int framenumber) const
294 {
295     //CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE
296     //Based on code by David Heidelberger, adapted from Andrew Duncan
297     //Given an int called framenumber and a double called framerate
298     //Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off.
299
300     int d;
301     int m;
302
303     //int framesPerHour = round(m_realFps * 60 * 60); //Number of frames in an hour
304     //int framesPer24Hours = framesPerHour * 24; //Number of frames in a day - timecode rolls over after 24 hours
305     int framesPer10Minutes = round(m_realFps * 60 * 10); //Number of frames per ten minutes
306     //int framesPerMinute = round(framerate)*60)-  dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames
307
308     /*
309      * The 2 check below should not be necessary in Kdenlive
310     if (framenumber<0) //Negative time. Add 24 hours.
311     {
312     framenumber=framesPer24Hours+framenumber;
313     }
314
315     //If framenumber is greater than 24 hrs, next operation will rollover clock
316     framenumber = framenumber % framesPer24Hours; //% is the modulus operator, which returns a remainder. a % b = the remainder of a/b
317     */
318
319     d = floor(framenumber / framesPer10Minutes); // \ means integer division, which is a/b without a remainder. Some languages you could use floor(a/b)
320     m = framenumber % framesPer10Minutes;
321
322     if (m > 1) {
323         framenumber = framenumber + (m_dropFrames * 9 * d) + m_dropFrames * (floor((m - m_dropFrames) / (round(m_realFps * 60) - m_dropFrames)));
324     } else {
325         framenumber = framenumber + m_dropFrames * 9 * d;
326     }
327
328     int frames = framenumber % m_displayedFramesPerSecond;
329     int seconds = (int) floor(framenumber / m_displayedFramesPerSecond) % 60;
330     int minutes = (int) floor(floor(framenumber / m_displayedFramesPerSecond) / 60) % 60;
331     int hours = floor(floor(floor(framenumber / m_displayedFramesPerSecond) / 60) / 60);
332
333
334     QString text;
335     text.append(QString::number(hours).rightJustified(2, '0', false));
336     text.append(':');
337     text.append(QString::number(minutes).rightJustified(2, '0', false));
338     text.append(':');
339     text.append(QString::number(seconds).rightJustified(2, '0', false));
340     text.append(';');
341     text.append(QString::number(frames).rightJustified(2, '0', false));
342
343     return text;
344 }