diff --git a/mainwindow.cpp b/mainwindow.cpp index 6a300cdd7..f4c76a839 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -3,6 +3,8 @@ #include "io/config.h" +#include "project/sequence.h" + #include "panels/panels.h" #include "panels/project.h" #include "panels/effectcontrols.h" @@ -85,6 +87,7 @@ MainWindow::MainWindow(QWidget *parent) : setup_layout(); connect(ui->menuWindow, SIGNAL(aboutToShow()), this, SLOT(windowMenu_About_To_Be_Shown())); + connect(ui->menu_View, SIGNAL(aboutToShow()), this, SLOT(viewMenu_About_To_Be_Shown())); QString data_dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); if (!data_dir.isEmpty()) { @@ -454,3 +457,37 @@ void MainWindow::windowMenu_About_To_Be_Shown() { ui->actionTimeline->setChecked(panel_timeline->isVisible()); ui->actionViewer->setChecked(panel_viewer->isVisible()); } + +void MainWindow::viewMenu_About_To_Be_Shown() { + ui->actionFrames->setChecked(panel_viewer->timecode_view == TIMECODE_FRAMES); + ui->actionDrop_Frame->setChecked(panel_viewer->timecode_view == TIMECODE_DROP); + if (sequence != NULL) ui->actionDrop_Frame->setEnabled(frame_rate_is_droppable(sequence->frame_rate)); + ui->actionNon_Drop_Frame->setChecked(panel_viewer->timecode_view == TIMECODE_NONDROP); +} + +void MainWindow::on_actionFrames_triggered() +{ + panel_viewer->timecode_view = TIMECODE_FRAMES; + if (sequence != NULL) { + panel_viewer->update_playhead_timecode(); + panel_viewer->update_end_timecode(); + } +} + +void MainWindow::on_actionDrop_Frame_triggered() +{ + panel_viewer->timecode_view = TIMECODE_DROP; + if (sequence != NULL) { + panel_viewer->update_playhead_timecode(); + panel_viewer->update_end_timecode(); + } +} + +void MainWindow::on_actionNon_Drop_Frame_triggered() +{ + panel_viewer->timecode_view = TIMECODE_NONDROP; + if (sequence != NULL) { + panel_viewer->update_playhead_timecode(); + panel_viewer->update_end_timecode(); + } +} diff --git a/mainwindow.h b/mainwindow.h index 6bce28352..ba01fdc2c 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -117,6 +117,14 @@ private slots: void windowMenu_About_To_Be_Shown(); + void on_actionFrames_triggered(); + + void on_actionDrop_Frame_triggered(); + + void on_actionNon_Drop_Frame_triggered(); + + void viewMenu_About_To_Be_Shown(); + private: Ui::MainWindow *ui; void setup_layout(); diff --git a/mainwindow.ui b/mainwindow.ui index f1cd0cb57..f8eb3c884 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -94,6 +94,10 @@ + + + + @@ -492,6 +496,30 @@ Ctrl+- + + + true + + + Frames + + + + + true + + + Drop-Frame + + + + + true + + + Non-Drop Frame + + diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 627b3a7bb..2dff352f6 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -215,25 +215,6 @@ void Timeline::redo() { } } -QString frame_to_timecode(long f) { - int hours = 0; - int mins = 0; - int secs = 0; - int frames = 0; - if (sequence != NULL) { - int int_fps = qRound(sequence->frame_rate); - hours = f/ (3600 * int_fps); - mins = f / (60*int_fps) % 60; - secs = f/int_fps % 60; - frames = f%int_fps; - } - return QString(QString::number(hours).rightJustified(2, '0') + - ":" + QString::number(mins).rightJustified(2, '0') + - ":" + QString::number(secs).rightJustified(2, '0') + - ":" + QString::number(frames).rightJustified(2, '0') - ); -} - void Timeline::repaint_timeline() { if (playing) { playhead = round(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * sequence->frame_rate)); @@ -245,7 +226,7 @@ void Timeline::repaint_timeline() { panel_viewer->viewer_widget->update(); last_frame = playhead; } - panel_viewer->ui->currentTimecode->setText(frame_to_timecode(playhead)); + panel_viewer->update_playhead_timecode(); } void Timeline::redraw_all_clips(bool changed) { @@ -261,7 +242,7 @@ void Timeline::redraw_all_clips(bool changed) { ui->audio_area->redraw_clips(); ui->headers->update(); - panel_viewer->ui->endTimecode->setText(frame_to_timecode(sequence->getEndFrame())); + panel_viewer->update_end_timecode(); } } diff --git a/panels/viewer.cpp b/panels/viewer.cpp index 9f3416c5c..9ba2a36af 100644 --- a/panels/viewer.cpp +++ b/panels/viewer.cpp @@ -6,6 +6,9 @@ #include "project/sequence.h" #include "panels/panels.h" +#define FRAMES_IN_ONE_MINUTE 1798 // 1800 - 2 +#define FRAMES_IN_TEN_MINUTES 17978 // (FRAMES_IN_ONE_MINUTE * 10) - 2 + extern "C" { #include } @@ -19,6 +22,7 @@ Viewer::Viewer(QWidget *parent) : ui->setupUi(this); ui->glViewerPane->child = ui->openGLWidget; viewer_widget = ui->openGLWidget; + timecode_view = TIMECODE_DROP; update_sequence(); } @@ -28,6 +32,81 @@ Viewer::~Viewer() delete ui; } +QString Viewer::frame_to_timecode(long f) { + if (timecode_view == TIMECODE_FRAMES) { + return QString::number(f); + } + + // return timecode + int hours = 0; + int mins = 0; + int secs = 0; + int frames = 0; + QString token = ":"; + + if (sequence != NULL) { + if (timecode_view == TIMECODE_DROP && frame_rate_is_droppable(sequence->frame_rate)) { + //CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE + //Code by David Heidelberger, adapted from Andrew Duncan, further adapted for Olive by Olive Team + //Given an int called framenumber and a double called framerate + //Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. + + int d; + int m; + + int dropFrames = round(sequence->frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate + int framesPerHour = round(sequence->frame_rate*60*60); //Number of frames in an hour + int framesPer24Hours = framesPerHour*24; //Number of frames in a day - timecode rolls over after 24 hours + int framesPer10Minutes = round(sequence->frame_rate * 60 * 10); //Number of frames per ten minutes + int framesPerMinute = (round(sequence->frame_rate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames + + //If framenumber is greater than 24 hrs, next operation will rollover clock + f = f % framesPer24Hours; //% is the modulus operator, which returns a remainder. a % b = the remainder of a/b + + d = f / framesPer10Minutes; // \ means integer division, which is a/b without a remainder. Some languages you could use floor(a/b) + m = f % framesPer10Minutes; + + //In the original post, the next line read m>1, which only worked for 29.97. Jean-Baptiste Mardelle correctly pointed out that m should be compared to dropFrames. + if (m > dropFrames) { + f = f + (dropFrames*9*d) + dropFrames * ((m - dropFrames) / framesPerMinute); + } else { + f = f + dropFrames*9*d; + } + + int frRound = round(sequence->frame_rate); + frames = f % frRound; + secs = (f / frRound) % 60; + mins = ((f / frRound) / 60) % 60; + hours = (((f / frRound) / 60) / 60); + + token = ";"; + } else { + int int_fps = qRound(sequence->frame_rate); + hours = f/ (3600 * int_fps); + mins = f / (60*int_fps) % 60; + secs = f/int_fps % 60; + frames = f%int_fps; + } + } + return QString(QString::number(hours).rightJustified(2, '0') + + ":" + QString::number(mins).rightJustified(2, '0') + + ":" + QString::number(secs).rightJustified(2, '0') + + token + QString::number(frames).rightJustified(2, '0') + ); +} + +bool frame_rate_is_droppable(float rate) { + return (rate == 23.976f || rate == 29.97f || rate == 59.94f); +} + +void Viewer::update_playhead_timecode() { + ui->currentTimecode->setText(frame_to_timecode(panel_timeline->playhead)); +} + +void Viewer::update_end_timecode() { + ui->endTimecode->setText(frame_to_timecode(sequence->getEndFrame())); +} + void Viewer::update_sequence() { bool null_sequence = (sequence == NULL); @@ -42,6 +121,15 @@ void Viewer::update_sequence() { init_audio(); if (!null_sequence) { + if (frame_rate_is_droppable(sequence->frame_rate)) { + timecode_view = TIMECODE_DROP; + } else { + timecode_view = TIMECODE_NONDROP; + } + + update_playhead_timecode(); + update_end_timecode(); + ui->glViewerPane->aspect_ratio = (float) sequence->width / (float) sequence->height; ui->glViewerPane->adjust(); } diff --git a/panels/viewer.h b/panels/viewer.h index 63be105bc..2f30bed66 100644 --- a/panels/viewer.h +++ b/panels/viewer.h @@ -1,6 +1,10 @@ #ifndef VIEWER_H #define VIEWER_H +#define TIMECODE_DROP 0 +#define TIMECODE_NONDROP 1 +#define TIMECODE_FRAMES 2 + #include class Timeline; @@ -11,6 +15,8 @@ namespace Ui { class Viewer; } +bool frame_rate_is_droppable(float rate); + class Viewer : public QDockWidget { Q_OBJECT @@ -21,6 +27,9 @@ public: void update_sequence(); void compose(); void set_playpause_icon(bool play); + int timecode_view; + void update_playhead_timecode(); + void update_end_timecode(); ViewerWidget* viewer_widget; @@ -36,8 +45,8 @@ private slots: void on_pushButton_3_clicked(); -private: - +private: + QString frame_to_timecode(long f); }; #endif // VIEWER_H diff --git a/panels/viewer.ui b/panels/viewer.ui index 8ef5d0b8c..7a748347c 100644 --- a/panels/viewer.ui +++ b/panels/viewer.ui @@ -82,7 +82,7 @@ - 00:00:00:00 + 0 @@ -299,7 +299,7 @@ - 00:00:00:00 + 0