]> git.sesse.net Git - kdenlive/blob - src/timecode.cpp
- Fix drop frame timecode format. [1]
[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     QRegExp regExp;
41     if (m_dropFrame)
42         regExp.setPattern("^\\d{2}:\\d{2}:\\d{2};\\d{2}$");
43     else
44         regExp.setPattern("^\\d{2}:\\d{2}:\\d{2}:\\d{2}$");
45     m_validator->setRegExp(regExp);
46 }
47
48 double Timecode::fps() const
49 {
50     return m_realFps; //m_displayedFramesPerSecond;
51 }
52
53 bool Timecode::df() const
54 {
55     return m_dropFrame;
56 }
57
58 const QValidator *Timecode::validator() const
59 {
60     return m_validator;
61 }
62
63
64
65 QString Timecode::reformatSeparators(QString duration) const
66 {
67     if (m_dropFrame)
68         return duration.replace(8, 1, ';');
69     return duration.replace(8, 1, ':');
70 }
71
72 int Timecode::getDisplayFrameCount(const QString duration, bool frameDisplay) const
73 {
74     if (frameDisplay) return duration.toInt();
75     return getFrameCount(duration);
76 }
77
78 int Timecode::getFrameCount(const QString duration) const
79 {
80     if (m_dropFrame) {
81         //Get Hours, Minutes, Seconds, Frames from timecode
82         int hours, minutes, seconds, frames;
83
84         hours = duration.section(':', 0, 0).toInt();
85         minutes = duration.section(':', 1, 1).toInt();
86         if (duration.contains(';')) {
87             seconds = duration.section(';', 0, 0).section(':', 2, 2).toInt();
88             frames = duration.section(';', 1, 1).toInt();
89         } else {
90             //Handle Drop Frame timecode frame calculations, even if the timecode supplied uses incorrect "99:99:99:99" format instead of "99:99:99;99"
91             seconds = duration.section(':', 2, 2).toInt();
92             frames = duration.section(':', 3, 3).toInt();
93         }
94
95         //Calculate the frame count
96         int dropRate = (int)((ceil(m_displayedFramesPerSecond) / 30) * 2);
97         frames += ((hours * 60 + minutes) * 60 + seconds) * m_displayedFramesPerSecond;
98         frames -= dropRate * ((hours * 60 + minutes) - (floor((hours * 60 + minutes) / 10)));
99         return frames;
100     }
101     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());
102 }
103
104 QString Timecode::getDisplayTimecode(const GenTime & time, bool frameDisplay) const
105 {
106     if (frameDisplay) return QString::number((int) time.frames(m_realFps));
107     return getTimecode(time);
108 }
109
110 QString Timecode::getTimecode(const GenTime & time) const
111 {
112     switch (m_format) {
113     case HH_MM_SS_FF:
114         return getTimecodeHH_MM_SS_FF(time);
115         break;
116     case HH_MM_SS_HH:
117         return getTimecodeHH_MM_SS_HH(time);
118         break;
119     case Frames:
120         return getTimecodeFrames(time);
121         break;
122     case Seconds:
123         return getTimecodeSeconds(time);
124         break;
125     default:
126         kWarning() <<
127         "Unknown timecode format specified, defaulting to HH_MM_SS_FF"
128         << endl;
129         return getTimecodeHH_MM_SS_FF(time);
130     }
131 }
132
133 const QString Timecode::getDisplayTimecodeFromFrames(int frames, bool frameDisplay) const
134 {
135     if (frameDisplay) return QString::number(frames);
136     return getTimecodeHH_MM_SS_FF(frames);
137 }
138
139 const QString Timecode::getTimecodeFromFrames(int frames) const
140 {
141     return getTimecodeHH_MM_SS_FF(frames);
142 }
143
144
145 //static
146 QString Timecode::getStringTimecode(int frames, const double &fps)
147 {
148     // Returns the timecode in an hh:mm:ss format
149     int seconds = (int)(frames / fps);
150     int minutes = seconds / 60;
151     seconds = seconds % 60;
152     int hours = minutes / 60;
153     minutes = minutes % 60;
154     QString text;
155     text.append(QString::number(hours).rightJustified(2, '0', false));
156     text.append(':');
157     text.append(QString::number(minutes).rightJustified(2, '0', false));
158     text.append(':');
159     text.append(QString::number(seconds).rightJustified(2, '0', false));
160     return text;
161 }
162
163
164 //static
165 QString Timecode::getEasyTimecode(const GenTime & time, const double &fps)
166 {
167     // Returns the timecode in an easily read display, like 3 min. 5 sec.
168     int frames = (int) time.frames(fps);
169     int seconds = (int)(frames / fps);
170     frames = frames - ((int)(fps * seconds));
171
172     int minutes = seconds / 60;
173     seconds = seconds % 60;
174     int hours = minutes / 60;
175     minutes = minutes % 60;
176
177     QString text;
178     bool trim = false;
179
180     if (hours != 0) {
181         text.append(QString::number(hours).rightJustified(2, '0', false));
182         text.append(' ' + i18n("hour") + ' ');
183         trim = true;
184     }
185     if (minutes != 0 || trim) {
186         if (!trim) {
187             text.append(QString::number(minutes));
188         } else
189             text.append(QString::number(minutes).rightJustified(2, '0', false));
190         text.append(' ' + i18n("min.") + ' ');
191         trim = true;
192     }
193     if (seconds != 0 || trim) {
194         if (!trim) {
195             text.append(QString::number(seconds));
196         } else
197             text.append(QString::number(seconds).rightJustified(2, '0', false));
198         text.append(' ' + i18n("sec."));
199         trim = true;
200     }
201     if (!trim) {
202         text.append(QString::number(frames));
203         text.append(' ' + i18n("frames"));
204     }
205
206     return text;
207 }
208
209
210 const QString Timecode::getTimecodeHH_MM_SS_FF(const GenTime & time) const
211 {
212     if (m_dropFrame)
213         return getTimecodeDropFrame(time);
214
215     return getTimecodeHH_MM_SS_FF((int) time.frames(m_realFps));
216 }
217
218 const QString Timecode::getTimecodeHH_MM_SS_FF(int frames) const
219 {
220     if (m_dropFrame) {
221         return getTimecodeDropFrame(frames);
222     }
223     int seconds = frames / m_displayedFramesPerSecond;
224     frames = frames % m_displayedFramesPerSecond;
225
226     int minutes = seconds / 60;
227     seconds = seconds % 60;
228     int hours = minutes / 60;
229     minutes = minutes % 60;
230
231     QString text;
232     text.append(QString::number(hours).rightJustified(2, '0', false));
233     text.append(':');
234     text.append(QString::number(minutes).rightJustified(2, '0', false));
235     text.append(':');
236     text.append(QString::number(seconds).rightJustified(2, '0', false));
237     text.append(':');
238     text.append(QString::number(frames).rightJustified(2, '0', false));
239
240     return text;
241 }
242
243 QString Timecode::getTimecodeHH_MM_SS_HH(const GenTime & time) const
244 {
245     int hundredths = (int)(time.seconds() * 100);
246     int seconds = hundredths / 100;
247     hundredths = hundredths % 100;
248     int minutes = seconds / 60;
249     seconds = seconds % 60;
250     int hours = minutes / 60;
251     minutes = minutes % 60;
252
253     QString text;
254
255     text.append(QString::number(hours).rightJustified(2, '0', false));
256     text.append(':');
257     text.append(QString::number(minutes).rightJustified(2, '0', false));
258     text.append(':');
259     text.append(QString::number(seconds).rightJustified(2, '0', false));
260     if (m_dropFrame)
261         text.append(';');
262     else
263         text.append(':');
264     text.append(QString::number(hundredths).rightJustified(2, '0', false));
265
266     return text;
267 }
268
269 QString Timecode::getTimecodeFrames(const GenTime & time) const
270 {
271     return QString::number(time.frames(m_realFps));
272 }
273
274 QString Timecode::getTimecodeSeconds(const GenTime & time) const
275 {
276     return QString::number(time.seconds());
277 }
278
279 QString Timecode::getTimecodeDropFrame(const GenTime & time) const
280 {
281     return getTimecodeDropFrame((int)time.frames(m_realFps));
282 }
283
284 QString Timecode::getTimecodeDropFrame(int frames) const
285 {
286     // Calculate the timecode using dropframes to remove the difference in fps. Note that this algorithm should work
287     // for NTSC times, but is untested for any others - it is in no way an "official" algorithm, unless it's by fluke.
288
289     // calculate how many frames need to be dropped every minute.
290     int dropRate = 0;
291     if (m_dropFrame) {
292         dropRate = (int)((ceil(m_displayedFramesPerSecond) / 30) * 2);
293     }
294
295     // calculate how many frames are in a normal minute, and how many are in a tenth minute.
296     int normalMinuteFrames = (m_displayedFramesPerSecond * 60) - dropRate;
297     int tenthMinuteFrames = (m_displayedFramesPerSecond * 60);
298
299     // Number of actual frames in a 10 minute interval :
300     int tenMinutes = (normalMinuteFrames * 9) + tenthMinuteFrames;
301
302     int tenMinuteIntervals = frames / tenMinutes;
303     frames = frames % tenMinutes;
304
305     int hours = tenMinuteIntervals / 6;
306     tenMinuteIntervals = tenMinuteIntervals % 6;
307
308     // At the point, we have figured out HH:M?:??:??
309
310     int numMinutes;
311
312     if (frames < tenthMinuteFrames) {
313         // tenth minute logic applies.
314         numMinutes = 0;
315     } else {
316         // normal minute logic applies.
317         numMinutes = 1 + (frames - tenthMinuteFrames) / normalMinuteFrames;
318         frames = (frames - tenthMinuteFrames) % normalMinuteFrames;
319         frames +=  dropRate;
320     }
321     // We now have HH:MM:??:??
322
323     int seconds = frames / m_displayedFramesPerSecond;
324     frames = frames % m_displayedFramesPerSecond;
325
326     // We now have HH:MM:SS:FF
327
328     // THANK FUCK FOR THAT.
329
330     QString text;
331     text.append(QString::number(hours).rightJustified(2, '0', false));
332     text.append(':');
333     text.append(QString::number(tenMinuteIntervals));
334     text.append(QString::number(numMinutes));
335     text.append(':');
336     text.append(QString::number(seconds).rightJustified(2, '0', false));
337     if (m_dropFrame)
338         text.append(';');
339     else
340         text.append(':');
341     text.append(QString::number(frames).rightJustified(2, '0', false));
342
343     return text;
344 }