/*** Olive - Non-Linear Video Editor Copyright (C) 2019 Olive Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "viewer.h" #include "rendering/audio.h" #include "timeline.h" #include "panels/project.h" #include "panels/effectcontrols.h" #include "timeline/sequence.h" #include "timeline/clip.h" #include "panels/panels.h" #include "global/config.h" #include "project/footage.h" #include "project/media.h" #include "undo/undo.h" #include "undo/undostack.h" #include "ui/audiomonitor.h" #include "rendering/renderfunctions.h" #include "ui/viewercontainer.h" #include "ui/labelslider.h" #include "ui/timelineheader.h" #include "ui/resizablescrollbar.h" #include "ui/icons.h" #include "global/global.h" #include "global/debug.h" #define FRAMES_IN_ONE_MINUTE 1798 // 1800 - 2 #define FRAMES_IN_TEN_MINUTES 17978 // (FRAMES_IN_ONE_MINUTE * 10) - 2 extern "C" { #include #include } #include #include #include #include #include #include #include Viewer::Viewer(QWidget *parent) : Panel(parent), playing(false), just_played_(false), media(nullptr), seq(nullptr), created_sequence(false), minimum_zoom(1.0), cue_recording_internal(false), playback_speed(0) { setup_ui(); headers->viewer = this; headers->snapping = false; headers->show_text(false); viewer_container->viewer = this; viewer_widget = viewer_container->child; viewer_widget->viewer = this; set_media(nullptr); current_timecode_slider->setEnabled(false); current_timecode_slider->SetMinimum(0); current_timecode_slider->SetDefault(qSNaN()); current_timecode_slider->SetValue(0); current_timecode_slider->SetDisplayType(LabelSlider::FrameNumber); connect(current_timecode_slider, SIGNAL(valueChanged(double)), this, SLOT(update_playhead())); recording_flasher.setInterval(500); connect(&playback_updater, SIGNAL(timeout()), this, SLOT(timer_update())); connect(&recording_flasher, SIGNAL(timeout()), this, SLOT(recording_flasher_update())); connect(horizontal_bar, SIGNAL(valueChanged(int)), headers, SLOT(set_scroll(int))); connect(horizontal_bar, SIGNAL(valueChanged(int)), viewer_widget, SLOT(set_waveform_scroll(int))); connect(horizontal_bar, SIGNAL(resize_move(double)), this, SLOT(resize_move(double))); update_playhead_timecode(0); update_end_timecode(); } Viewer::~Viewer() {} void Viewer::Retranslate() { /// Viewer panels are retranslated through the MainWindow to differentiate Media and Sequence Viewers // update_window_title(); } bool Viewer::is_focused() { return headers->hasFocus() || viewer_widget->hasFocus() || go_to_start_button->hasFocus() || prev_frame_button->hasFocus() || play_button->hasFocus() || next_frame_button->hasFocus() || go_to_end_frame->hasFocus(); } bool Viewer::is_main_sequence() { return main_sequence; } void Viewer::set_main_sequence() { clean_created_seq(); set_sequence(true, olive::ActiveSequence); } void Viewer::reset_all_audio() { // reset all clip audio if (seq != nullptr) { long last_frame = 0; for (int i=0;iclips.size();i++) { ClipPtr c = seq->clips.at(i); if (c != nullptr) { c->reset_audio(); last_frame = qMax(last_frame, c->timeline_out()); } } audio_ibuffer_frame = seq->playhead; if (playback_speed < 0) { audio_ibuffer_frame = last_frame - audio_ibuffer_frame; } audio_ibuffer_timecode = double(audio_ibuffer_frame) / seq->frame_rate; } clear_audio_ibuffer(); } long timecode_to_frame(const QString& s, int view, double frame_rate) { QList list = s.split(QRegExp("[:;]")); if (view == olive::kTimecodeFrames || (list.size() == 1 && view != olive::kTimecodeMilliseconds)) { return s.toLong(); } int frRound = qRound(frame_rate); int hours, minutes, seconds, frames; if (view == olive::kTimecodeMilliseconds) { long milliseconds = s.toLong(); hours = milliseconds/3600000; milliseconds -= (hours*3600000); minutes = milliseconds/60000; milliseconds -= (minutes*60000); seconds = milliseconds/1000; milliseconds -= (seconds*1000); frames = qRound64((milliseconds*0.001)*frame_rate); seconds = qRound64(seconds * frame_rate); minutes = qRound64(minutes * frame_rate * 60); hours = qRound64(hours * frame_rate * 3600); } else { hours = ((list.size() > 0) ? list.at(0).toInt() : 0) * frRound * 3600; minutes = ((list.size() > 1) ? list.at(1).toInt() : 0) * frRound * 60; seconds = ((list.size() > 2) ? list.at(2).toInt() : 0) * frRound; frames = (list.size() > 3) ? list.at(3).toInt() : 0; } int f = (frames + seconds + minutes + hours); if ((view == olive::kTimecodeDrop || view == olive::kTimecodeMilliseconds) && frame_rate_is_droppable(frame_rate)) { // return drop int d; int m; int dropFrames = qRound(frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate int framesPer10Minutes = qRound(frame_rate * 60 * 10); //Number of frames per ten minutes int framesPerMinute = (qRound(frame_rate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames d = f / framesPer10Minutes; f -= dropFrames*9*d; m = f % framesPer10Minutes; if (m > dropFrames) { f -= (dropFrames * ((m - dropFrames) / framesPerMinute)); } } // return non-drop return f; } QString frame_to_timecode(long f, int view, double frame_rate) { if (view == olive::kTimecodeFrames) { return QString::number(f); } // return timecode int hours = 0; int mins = 0; int secs = 0; int frames = 0; QString token = ":"; if ((view == olive::kTimecodeDrop || view == olive::kTimecodeMilliseconds) && frame_rate_is_droppable(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 = qRound(frame_rate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate int framesPerHour = qRound(frame_rate*60*60); //Number of frqRound64ames in an hour int framesPer24Hours = framesPerHour*24; //Number of frames in a day - timecode rolls over after 24 hours int framesPer10Minutes = qRound(frame_rate * 60 * 10); //Number of frames per ten minutes int framesPerMinute = (qRound(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 = qRound(frame_rate); frames = f % frRound; secs = (f / frRound) % 60; mins = ((f / frRound) / 60) % 60; hours = (((f / frRound) / 60) / 60); token = ";"; } else { // non-drop timecode int int_fps = qRound(frame_rate); hours = f / (3600 * int_fps); mins = f / (60*int_fps) % 60; secs = f/int_fps % 60; frames = f%int_fps; } if (view == olive::kTimecodeMilliseconds) { return QString::number((hours*3600000)+(mins*60000)+(secs*1000)+qCeil(frames*1000/frame_rate)); } 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(double rate) { return (qFuzzyCompare(rate, 23.976) || qFuzzyCompare(rate, 29.97) || qFuzzyCompare(rate, 59.94)); } void Viewer::seek(long p) { pause(); if (main_sequence) { seq->playhead = p; } else { seq->playhead = qMin(seq->getEndFrame(), qMax(0L, p)); } bool update_fx = false; if (main_sequence) { panel_timeline->scroll_to_frame(p); panel_effect_controls->scroll_to_frame(p); if (olive::CurrentConfig.seek_also_selects) { panel_timeline->select_from_playhead(); update_fx = true; } } reset_all_audio(); audio_scrub = true; last_playhead = seq->playhead; update_parents(update_fx); } void Viewer::go_to_start() { if (seq != nullptr) seek(0); } void Viewer::go_to_end() { if (seq != nullptr) seek(seq->getEndFrame()); } void Viewer::close_media() { set_media(nullptr); } void Viewer::go_to_in() { if (seq != nullptr) { if (seq->using_workarea) { seek(seq->workarea_in); } else { go_to_start(); } } } void Viewer::previous_frame() { if (seq != nullptr && seq->playhead > 0) seek(seq->playhead-1); } void Viewer::next_frame() { if (seq != nullptr) seek(seq->playhead+1); } void Viewer::go_to_out() { if (seq != nullptr) { if (seq->using_workarea) { seek(seq->workarea_out); } else { go_to_end(); } } } void Viewer::cue_recording(long start, long end, int track) { recording_start = start; recording_end = end; recording_track = track; panel_sequence_viewer->seek(recording_start); cue_recording_internal = true; recording_flasher.start(); } void Viewer::uncue_recording() { cue_recording_internal = false; recording_flasher.stop(); play_button->setStyleSheet(QString()); } bool Viewer::is_recording_cued() { return cue_recording_internal; } void Viewer::toggle_play() { if (playing) { pause(); } else { play(); } } void Viewer::increase_speed() { int new_speed = playback_speed+1; if (new_speed == 0) { new_speed++; } set_playback_speed(new_speed); } void Viewer::decrease_speed() { int new_speed = playback_speed-1; if (new_speed == 0) { new_speed--; } set_playback_speed(new_speed); } void Viewer::play(bool in_to_out) { if (seq == nullptr) { return; } if (panel_sequence_viewer->playing) panel_sequence_viewer->pause(); if (panel_footage_viewer->playing) panel_footage_viewer->pause(); long sequence_end_frame = seq->getEndFrame(); if (sequence_end_frame == 0) { return; } playing_in_to_out = in_to_out; if (seq != nullptr) { if (playing_in_to_out) { uncue_recording(); } bool seek_to_in = (seq->using_workarea && (olive::CurrentConfig.loop || playing_in_to_out)); if (!is_recording_cued() && playback_speed >= 0 && (playing_in_to_out || seq->playhead >= sequence_end_frame || (seek_to_in && seq->playhead >= seq->workarea_out))) { seek(seek_to_in ? seq->workarea_in : 0); } if (playback_speed == 0) { playback_speed = 1; } reset_all_audio(); if (is_recording_cued() && !start_recording()) { qCritical() << "Failed to record audio"; return; } playhead_start = seq->playhead; playing = true; just_played_ = true; set_playpause_icon(false); start_msecs = QDateTime::currentMSecsSinceEpoch(); timer_update(); } } void Viewer::play_wake() { if (just_played_) { start_msecs = QDateTime::currentMSecsSinceEpoch(); playback_updater.start(); if (audio_thread != nullptr) audio_thread->notifyReceiver(); just_played_ = false; } } void Viewer::pause() { playing = false; just_played_ = false; set_playpause_icon(true); playback_updater.stop(); playback_speed = 0; if (is_recording_cued()) { uncue_recording(); if (recording) { stop_recording(); // import audio QStringList file_list; file_list.append(get_recorded_audio_filename()); panel_project->process_file_list(file_list); // add it to the sequence ClipPtr c = std::make_shared(seq.get()); Media* m = panel_project->last_imported_media.at(0); Footage* f = m->to_footage(); // wait for footage to be completely ready before taking metadata from it f->ready_lock.lock(); c->set_media(m, 0); // latest media c->set_timeline_in(recording_start); c->set_timeline_out(recording_start + f->get_length_in_frames(seq->frame_rate)); c->set_clip_in(0); c->set_track(recording_track); c->set_color(128, 192, 128); c->set_name(m->get_name()); f->ready_lock.unlock(); QVector add_clips; add_clips.append(c); olive::UndoStack.push(new AddClipCommand(seq.get(), add_clips)); // add clip } } } bool Viewer::WaitingForPlayWake() { return just_played_; } void Viewer::update_playhead_timecode(long p) { current_timecode_slider->SetValue(p); } void Viewer::update_end_timecode() { end_timecode->setText((seq == nullptr) ? frame_to_timecode(0, olive::CurrentConfig.timecode_view, 30) : frame_to_timecode(seq->getEndFrame(), olive::CurrentConfig.timecode_view, seq->frame_rate)); } void Viewer::update_header_zoom() { if (seq != nullptr) { long sequenceEndFrame = seq->getEndFrame(); if (cached_end_frame != sequenceEndFrame) { minimum_zoom = (sequenceEndFrame > 0) ? ((double) headers->width() / (double) sequenceEndFrame) : 1; headers->update_zoom(qMax(headers->get_zoom(), minimum_zoom)); set_sb_max(); viewer_widget->waveform_zoom = headers->get_zoom(); } else { headers->update(); } } } void Viewer::update_parents(bool reload_fx) { if (main_sequence) { update_ui(reload_fx); } else { update_viewer(); panel_timeline->repaint_timeline(); } } int Viewer::get_playback_speed() { return playback_speed; } void Viewer::set_marker() { set_marker_internal(seq.get()); } void Viewer::resizeEvent(QResizeEvent *e) { QDockWidget::resizeEvent(e); if (seq != nullptr) { set_sb_max(); viewer_widget->update(); } } void Viewer::update_viewer() { update_header_zoom(); viewer_widget->frame_update(); if (seq != nullptr) { update_playhead_timecode(seq->playhead); } update_end_timecode(); } void Viewer::clear_in() { if (seq != nullptr && seq->using_workarea) { olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), true, 0, seq->workarea_out)); update_parents(); } } void Viewer::clear_out() { if (seq != nullptr && seq->using_workarea) { olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), true, seq->workarea_in, seq->getEndFrame())); update_parents(); } } void Viewer::clear_inout_point() { if (seq != nullptr && seq->using_workarea) { olive::UndoStack.push(new SetTimelineInOutCommand(seq.get(), false, 0, 0)); update_parents(); } } void Viewer::set_in_point() { if (seq != nullptr) { headers->set_in_point(seq->playhead); } } void Viewer::set_out_point() { if (seq != nullptr) { headers->set_out_point(seq->playhead); } } void Viewer::set_zoom(bool in) { if (seq != nullptr) { set_zoom_value(in ? headers->get_zoom()*2 : qMax(minimum_zoom, headers->get_zoom()*0.5)); } } void Viewer::set_panel_name(const QString &n) { panel_name = n; update_window_title(); } void Viewer::update_window_title() { QString name; if (seq == nullptr) { name = tr("(none)"); } else { name = seq->name; } setWindowTitle(QString("%1: %2").arg(panel_name, name)); } void Viewer::set_zoom_value(double d) { headers->update_zoom(d); if (viewer_widget->waveform) { viewer_widget->waveform_zoom = d; viewer_widget->update(); } if (seq != nullptr) { set_sb_max(); if (!horizontal_bar->is_resizing()) center_scroll_to_playhead(horizontal_bar, headers->get_zoom(), seq->playhead); } } void Viewer::set_sb_max() { headers->set_scrollbar_max(horizontal_bar, seq->getEndFrame(), headers->width()); } void Viewer::set_playback_speed(int s) { pause(); playback_speed = qMin(3, qMax(s, -3)); if (playback_speed != 0) { play(); } } long Viewer::get_seq_in() { return ((olive::CurrentConfig.loop || playing_in_to_out) && seq->using_workarea) ? seq->workarea_in : 0; } long Viewer::get_seq_out() { return ((olive::CurrentConfig.loop || playing_in_to_out) && seq->using_workarea && previous_playhead < seq->workarea_out) ? seq->workarea_out : seq->getEndFrame(); } void Viewer::setup_ui() { QWidget* contents = new QWidget(); QVBoxLayout* layout = new QVBoxLayout(contents); layout->setSpacing(0); layout->setMargin(0); setWidget(contents); viewer_container = new ViewerContainer(); viewer_container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout->addWidget(viewer_container); headers = new TimelineHeader(); layout->addWidget(headers); horizontal_bar = new ResizableScrollBar(); horizontal_bar->setSingleStep(20); horizontal_bar->setOrientation(Qt::Horizontal); layout->addWidget(horizontal_bar); QWidget* lower_controls = new QWidget(); QHBoxLayout* lower_control_layout = new QHBoxLayout(lower_controls); lower_control_layout->setMargin(0); // current time code QWidget* current_timecode_container = new QWidget(); QHBoxLayout* current_timecode_container_layout = new QHBoxLayout(current_timecode_container); current_timecode_container_layout->setSpacing(0); current_timecode_container_layout->setMargin(0); current_timecode_slider = new LabelSlider(); current_timecode_container_layout->addWidget(current_timecode_slider); lower_control_layout->addWidget(current_timecode_container); QWidget* playback_controls = new QWidget(); QHBoxLayout* playback_control_layout = new QHBoxLayout(playback_controls); playback_control_layout->setSpacing(0); playback_control_layout->setMargin(0); go_to_start_button = new QPushButton(); go_to_start_button->setIcon(olive::icon::ViewerGoToStart); connect(go_to_start_button, SIGNAL(clicked(bool)), this, SLOT(go_to_in())); playback_control_layout->addWidget(go_to_start_button); prev_frame_button = new QPushButton(); prev_frame_button->setIcon(olive::icon::ViewerPrevFrame); connect(prev_frame_button, SIGNAL(clicked(bool)), this, SLOT(previous_frame())); playback_control_layout->addWidget(prev_frame_button); play_button = new QPushButton(); play_button->setIcon(olive::icon::ViewerPlay); connect(play_button, SIGNAL(clicked(bool)), this, SLOT(toggle_play())); playback_control_layout->addWidget(play_button); next_frame_button = new QPushButton(); next_frame_button->setIcon(olive::icon::ViewerNextFrame); connect(next_frame_button, SIGNAL(clicked(bool)), this, SLOT(next_frame())); playback_control_layout->addWidget(next_frame_button); go_to_end_frame = new QPushButton(); go_to_end_frame->setIcon(olive::icon::ViewerGoToEnd); connect(go_to_end_frame, SIGNAL(clicked(bool)), this, SLOT(go_to_out())); playback_control_layout->addWidget(go_to_end_frame); lower_control_layout->addWidget(playback_controls); QWidget* end_timecode_container = new QWidget(); QHBoxLayout* end_timecode_layout = new QHBoxLayout(end_timecode_container); end_timecode_layout->setSpacing(0); end_timecode_layout->setMargin(0); end_timecode = new QLabel(); end_timecode->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); end_timecode_layout->addWidget(end_timecode); lower_control_layout->addWidget(end_timecode_container); layout->addWidget(lower_controls); } void Viewer::set_media(Media* m) { main_sequence = false; media = m; clean_created_seq(); if (media != nullptr) { switch (media->get_type()) { case MEDIA_TYPE_FOOTAGE: { Footage* footage = media->to_footage(); marker_ref = &footage->markers; seq = std::make_shared(); created_sequence = true; seq->wrapper_sequence = true; seq->name = footage->name; seq->using_workarea = footage->using_inout; if (footage->using_inout) { seq->workarea_in = footage->in; seq->workarea_out = footage->out; } // FIXME: Move this magic number to Config seq->frame_rate = 30; if (footage->video_tracks.size() > 0) { const FootageStream& video_stream = footage->video_tracks.at(0); seq->width = video_stream.video_width; seq->height = video_stream.video_height; if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate * footage->speed; ClipPtr c = std::make_shared(seq.get()); c->set_media(media, video_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(seq->frame_rate)); if (c->timeline_out() <= 0) { // FIXME: Move this magic number to Config c->set_timeline_out(150); } c->set_track(-1); c->set_clip_in(0); c->refresh(); seq->clips.append(c); } else { // FIXME: Move this magic number to Config seq->width = 1920; seq->height = 1080; } if (footage->audio_tracks.size() > 0) { const FootageStream& audio_stream = footage->audio_tracks.at(0); seq->audio_frequency = audio_stream.audio_frequency; ClipPtr c = std::make_shared(seq.get()); c->set_media(media, audio_stream.file_index); c->set_timeline_in(0); c->set_timeline_out(footage->get_length_in_frames(seq->frame_rate)); c->set_track(0); c->set_clip_in(0); c->refresh(); seq->clips.append(c); if (footage->video_tracks.size() == 0) { viewer_widget->waveform = true; viewer_widget->waveform_clip = c; viewer_widget->waveform_ms = &audio_stream; viewer_widget->frame_update(); } } else { // FIXME: Move this magic number to Config seq->audio_frequency = 48000; } seq->audio_layout = AV_CH_LAYOUT_STEREO; } break; case MEDIA_TYPE_SEQUENCE: seq = media->to_sequence(); break; } } set_sequence(false, seq); } void Viewer::update_playhead() { seek(current_timecode_slider->value()); } void Viewer::timer_update() { previous_playhead = seq->playhead; seq->playhead = qMax(0, qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate * playback_speed))); if (olive::CurrentConfig.seek_also_selects) panel_timeline->select_from_playhead(); update_parents(olive::CurrentConfig.seek_also_selects); if (playing) { if (playback_speed < 0 && seq->playhead == 0) { pause(); } else if (recording) { if (recording_start != recording_end && seq->playhead >= recording_end) { pause(); } } else if (playback_speed > 0) { if (seq->playhead >= seq->getEndFrame()) { pause(); } if (seq->using_workarea && seq->playhead >= seq->workarea_out) { if (olive::CurrentConfig.loop) { // loop play(); } else if (playing_in_to_out) { pause(); } } } } } void Viewer::recording_flasher_update() { if (play_button->styleSheet().isEmpty()) { play_button->setStyleSheet("background: red;"); } else { play_button->setStyleSheet(QString()); } } void Viewer::resize_move(double d) { set_zoom_value(headers->get_zoom()*d); } void Viewer::clean_created_seq() { viewer_widget->waveform = false; if (created_sequence) { // TODO delete undo commands referencing this sequence to avoid crashes /* for (int i=0;isetEnabled(!null_sequence); current_timecode_slider->setEnabled(!null_sequence); viewer_widget->setEnabled(!null_sequence); viewer_widget->setVisible(!null_sequence); go_to_start_button->setEnabled(!null_sequence); prev_frame_button->setEnabled(!null_sequence); play_button->setEnabled(!null_sequence); next_frame_button->setEnabled(!null_sequence); go_to_end_frame->setEnabled(!null_sequence); if (!null_sequence) { current_timecode_slider->SetFrameRate(seq->frame_rate); playback_updater.setInterval(qFloor(1000 / seq->frame_rate)); update_playhead_timecode(seq->playhead); update_end_timecode(); viewer_container->adjust(); if (!created_sequence) { marker_ref = &seq->markers; } } else { update_playhead_timecode(0); update_end_timecode(); } update_window_title(); update_header_zoom(); viewer_widget->frame_update(); update(); } void Viewer::set_playpause_icon(bool play) { play_button->setIcon(play ? olive::icon::ViewerPlay : olive::icon::ViewerPause); }