diff --git a/olive.pro.user b/olive.pro.user index 38f91c8f9..936df18b6 100644 --- a/olive.pro.user +++ b/olive.pro.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/panels/project.cpp b/panels/project.cpp index 21d2e89f5..51313d153 100644 --- a/panels/project.cpp +++ b/panels/project.cpp @@ -358,7 +358,8 @@ void Project::load_project() { Clip* temp_clip; int state = LOAD_STATE_IDLE; - while (!stream.atEnd()) { + bool error = false; + while (!error && !stream.atEnd()) { stream.readNext(); switch (state) { case LOAD_STATE_IDLE: @@ -380,9 +381,13 @@ void Project::load_project() { case LOAD_STATE_FOOTAGE: if (stream.isEndElement() && stream.name() == "footage") { Media* m = import_file(temp_url); - m->save_id = temp_media_id; - m->name = temp_name; - temp_media_list.append(m); + if (m == NULL) { + error = (QMessageBox::warning(this, "Source load error", "Couldn't load source file '" + temp_url + "'. This is unsupported and may lead to crashes. Do you wish to continue?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No); + } else { + m->save_id = temp_media_id; + m->name = temp_name; + temp_media_list.append(m); + } state = LOAD_STATE_IDLE; } else if (stream.isStartElement()) { if (stream.name() == "name") { @@ -410,9 +415,12 @@ void Project::load_project() { break; } } - if (!found) { - clip->linked.removeAt(j); - qDebug() << "[WARNING] Discarded link from loaded file - this should NEVER happen"; + if (!found) { + if (QMessageBox::warning(this, "Corrupt clip link", "Project contains a non-existent clip link. It may be corrupt. Would you like to load it anyway?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + clip->linked.removeAt(j); + } else { + error = true; + } } } } @@ -497,9 +505,13 @@ void Project::load_project() { } else if (stream.name() == "stream") { stream.readNext(); int stream_index = stream.text().toInt(); - temp_clip->media_stream = temp_clip->media->get_stream_from_file_index(stream_index); - if (temp_clip->media_stream == NULL) { - qDebug() << "[WARNING] Could not load media stream - project file seems corrupt"; + if (temp_clip->media != NULL) { + temp_clip->media_stream = temp_clip->media->get_stream_from_file_index(stream_index); + if (temp_clip->media_stream == NULL) { + if (QMessageBox::warning(this, "Source changed", "Source file '" + temp_clip->media->url + "' appears to have changed since last saving the project. This is unsupported and may lead to crashes. Do you wish to continue?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) { + error = true; + } + } } } else if (stream.name() == "linked") { state = LOAD_STATE_CLIP_LINKS; @@ -531,11 +543,16 @@ void Project::load_project() { } if (stream.hasError()) { qDebug() << "[ERROR] Error parsing XML." << stream.error(); + QMessageBox::critical(this, "XML Parsing Error", "Couldn't load '" + project_url + "'. " + stream.error(), QMessageBox::Ok); + error = true; } - panel_timeline->redraw_all_clips(false); - - project_changed = false; + if (error) { + new_project(); + } else { + panel_timeline->redraw_all_clips(false); + project_changed = false; + } } void Project::save_project() { diff --git a/panels/timeline.cpp b/panels/timeline.cpp index 63dc8633e..b354bd242 100644 --- a/panels/timeline.cpp +++ b/panels/timeline.cpp @@ -29,7 +29,7 @@ Timeline::Timeline(QWidget *parent) : splitting = false; importing = false; playing = false; - trim_in = false; + trim_in_point = false; snapped = false; rect_select_init = false; rect_select_proc = false; diff --git a/panels/timeline.h b/panels/timeline.h index 1e21753a3..7dce03268 100644 --- a/panels/timeline.h +++ b/panels/timeline.h @@ -34,6 +34,7 @@ struct Ghost { // other variables long ghost_length; long media_length; + bool trim_in; }; struct Selection { @@ -44,6 +45,8 @@ struct Selection { long old_in; long old_out; int old_track; + + bool trim_in; }; namespace Ui { @@ -141,11 +144,11 @@ public: // moving bool moving_init; bool moving_proc; - QVector ghosts; + QVector ghosts; // trimming int trim_target; - bool trim_in; + bool trim_in_point; // splitting bool splitting; diff --git a/project/clip.cpp b/project/clip.cpp index e99da404c..e074376cd 100644 --- a/project/clip.cpp +++ b/project/clip.cpp @@ -23,6 +23,7 @@ Clip::Clip() { texture = NULL; opening_transition = NULL; closing_transition = NULL; + media = NULL; pkt = new AVPacket(); } diff --git a/ui/audiomonitor.cpp b/ui/audiomonitor.cpp new file mode 100644 index 000000000..7ca2ef027 --- /dev/null +++ b/ui/audiomonitor.cpp @@ -0,0 +1,63 @@ +#include "audiomonitor.h" + +#include "project/sequence.h" +#include "playback/audio.h" +#include "panels/panels.h" +#include "panels/timeline.h" + +#include +#include +#include +#include + +#define AUDIO_MONITOR_GAP 3 + +extern "C" { +#include "libavformat/avformat.h" +} + +AudioMonitor::AudioMonitor(QWidget *parent) : QWidget(parent) { +} + +void AudioMonitor::reset() { + sample_cache_offset = -1; + sample_cache.clear(); +} + +void AudioMonitor::resizeEvent(QResizeEvent *e) { + gradient = QLinearGradient(QPoint(0, rect().top()), QPoint(0, rect().bottom())); + gradient.setColorAt(0, Qt::red); + gradient.setColorAt(0.25, Qt::yellow); + gradient.setColorAt(1, Qt::green); + QWidget::resizeEvent(e); +} + +void AudioMonitor::paintEvent(QPaintEvent *) { + if (sequence != NULL) { + QPainter p(this); + int channel_x = AUDIO_MONITOR_GAP; + int channel_count = av_get_channel_layout_nb_channels(sequence->audio_layout); + int channel_width = (width()/channel_count) - AUDIO_MONITOR_GAP; +// for (int i=0;iplayhead - sample_cache_offset; + if (playhead_offset >= 0 && playhead_offset < sample_cache.size()) { + //int channel_offset = i*2; + double multiplier = 1 - qAbs((double) sample_cache.at(playhead_offset) / 32768.0); // 16-bit int divided to float + r.setHeight(r.height()*multiplier); + sample_cache_offset++; + sample_cache.removeFirst(); + } else { + reset(); + } + } + + p.fillRect(r, QColor(0, 0, 0, 160)); + channel_x += channel_width + AUDIO_MONITOR_GAP; + } + } +} diff --git a/ui/audiomonitor.h b/ui/audiomonitor.h new file mode 100644 index 000000000..c22212412 --- /dev/null +++ b/ui/audiomonitor.h @@ -0,0 +1,27 @@ +#ifndef AUDIOMONITOR_H +#define AUDIOMONITOR_H + +#include + +class AudioMonitor : public QWidget +{ + Q_OBJECT +public: + explicit AudioMonitor(QWidget *parent = 0); + QVector sample_cache; + long sample_cache_offset; + void reset(); + +protected: + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + +signals: + +public slots: + +private: + QLinearGradient gradient; +}; + +#endif // AUDIOMONITOR_H diff --git a/ui/timelinewidget.cpp b/ui/timelinewidget.cpp index 46b4ba6ce..133797ec9 100644 --- a/ui/timelinewidget.cpp +++ b/ui/timelinewidget.cpp @@ -210,6 +210,7 @@ void TimelineWidget::mousePressEvent(QMouseEvent *event) { case TIMELINE_TOOL_POINTER: case TIMELINE_TOOL_RIPPLE: case TIMELINE_TOOL_SLIP: + case TIMELINE_TOOL_ROLLING: { if (track_resizing) { track_resize_mouse_cache = event->pos().y(); @@ -295,24 +296,26 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { // ripple_length becomes the length/number of frames we trimmed // ripple point becomes the point to ripple (i.e. the point after or before which we move every clip) - if (panel_timeline->trim_in) { - ripple_length = panel_timeline->ghosts.at(0).old_in - panel_timeline->ghosts.at(0).in; - ripple_point = panel_timeline->ghosts.at(0).old_in; + const Ghost& first_ghost = panel_timeline->ghosts.at(0); + if (panel_timeline->trim_in_point) { + ripple_length = first_ghost.old_in - panel_timeline->ghosts.at(0).in; + ripple_point = first_ghost.old_in; } else { // if we're trimming an out-point - ripple_length = panel_timeline->ghosts.at(0).old_out - panel_timeline->ghosts.at(0).out; - ripple_point = panel_timeline->ghosts.at(0).old_out; + ripple_length = first_ghost.old_out - panel_timeline->ghosts.at(0).out; + ripple_point = first_ghost.old_out; } for (int i=0;ighosts.size();i++) { + const Ghost& g = panel_timeline->ghosts.at(i); long comp_point; - if (panel_timeline->trim_in) { - comp_point = panel_timeline->ghosts.at(i).old_in; + if (panel_timeline->trim_in_point) { + comp_point = g.old_in; } else { - comp_point = panel_timeline->ghosts.at(i).old_out; + comp_point = g.old_out; } if (ripple_point > comp_point) ripple_point = comp_point; } - if (!panel_timeline->trim_in) ripple_length = -ripple_length; + if (!panel_timeline->trim_in_point) ripple_length = -ripple_length; panel_timeline->ripple(ripple_point, ripple_length); } @@ -467,10 +470,11 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *event) { void TimelineWidget::init_ghosts() { for (int i=0;ighosts.size();i++) { Ghost& g = panel_timeline->ghosts[i]; - g.old_in = g.in; - g.old_out = g.out; - g.old_track = g.track; - g.old_clip_in = g.clip_in; + Clip* c = sequence->get_clip(g.clip); + g.in = g.old_in = c->timeline_in; + g.out = g.old_out = c->timeline_out; + g.track = g.old_track = c->track; + g.clip_in = g.old_clip_in = c->clip_in; if (panel_timeline->trim_target > -1 || panel_timeline->tool == TIMELINE_TOOL_SLIP) { // used for trim ops @@ -491,12 +495,12 @@ bool subvalidate_snapping(Ghost& g, long* frame_diff, long snap_point) { long in_validator = g.old_in + *frame_diff - snap_point; long out_validator = g.old_out + *frame_diff - snap_point; - if ((panel_timeline->trim_target == -1 || panel_timeline->trim_in) && in_validator > -snap_range && in_validator < snap_range) { + if ((panel_timeline->trim_target == -1 || g.trim_in) && in_validator > -snap_range && in_validator < snap_range) { *frame_diff -= in_validator; panel_timeline->snap_point = snap_point; panel_timeline->snapped = true; return true; - } else if (!panel_timeline->trim_in && out_validator > -snap_range && out_validator < snap_range) { + } else if (!g.trim_in && out_validator > -snap_range && out_validator < snap_range) { *frame_diff -= out_validator; panel_timeline->snap_point = snap_point; panel_timeline->snapped = true; @@ -538,7 +542,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { validate_snapping(g, &frame_diff); - if (panel_timeline->trim_in) { + if (g.trim_in) { // prevent clip length from being less than 1 frame long validator = g.ghost_length - frame_diff; if (validator < 1) frame_diff -= (1 - validator); @@ -576,7 +580,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { for (int k=0;ktrack == post->track) { - if (panel_timeline->trim_in) { + if (panel_timeline->trim_in_point) { validator = post->timeline_in - frame_diff - pre->timeline_out; if (validator < 0) frame_diff += validator; } else { @@ -593,7 +597,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { for (int i=0;ighosts.size();i++) { Ghost& g = panel_timeline->ghosts[i]; - if (panel_timeline->trim_in) { + if (g.trim_in) { g.in = g.old_in + frame_diff; g.clip_in = g.old_clip_in + frame_diff; } else { @@ -605,7 +609,7 @@ void TimelineWidget::update_ghosts(QPoint& mouse_pos) { for (int i=0;iselections.size();i++) { Selection& s = panel_timeline->selections[i]; - if (panel_timeline->trim_in) { + if (panel_timeline->trim_in_point) { s.in = s.old_in + frame_diff; } else { s.out = s.old_out + frame_diff; @@ -780,14 +784,45 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { if (c != NULL && panel_timeline->is_clip_selected(c)) { Ghost g; g.clip = i; - g.in = c->timeline_in; - g.out = c->timeline_out; - g.track = c->track; - g.clip_in = c->clip_in; + g.trim_in = panel_timeline->trim_in_point; panel_timeline->ghosts.append(g); } } + if (panel_timeline->tool == TIMELINE_TOOL_ROLLING) { + int size = panel_timeline->ghosts.size(); + for (int i=0;iget_clip(panel_timeline->ghosts.at(i).clip); + for (int j=0;jclip_count();j++) { + Clip* comp_clip = sequence->get_clip(j); + if (comp_clip->track == ghost_clip->track) { + if ((panel_timeline->trim_in_point && comp_clip->timeline_out == ghost_clip->timeline_in) || + (!panel_timeline->trim_in_point && comp_clip->timeline_in == ghost_clip->timeline_out)) { + // see if this clip is already selected, and if so just switch the trim_in + bool found = false; + for (int k=0;kghosts.at(k).clip == j) { + panel_timeline->ghosts[k].trim_in = !panel_timeline->trim_in_point; + found = true; + break; + } + } + + if (!found) { + // add ghost for this clip with opposite trim_in + Ghost g; + g.clip = j; + g.trim_in = !panel_timeline->trim_in_point; + panel_timeline->ghosts.append(g); + } + } + } + } + } + } + + init_ghosts(); + // ripple edit prep if (panel_timeline->tool == TIMELINE_TOOL_RIPPLE) { QVector axis_ghosts; @@ -797,8 +832,8 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { for (int j=0;jtrack == cc->track && - ((panel_timeline->trim_in && c->timeline_in < cc->timeline_in) || - (!panel_timeline->trim_in && c->timeline_out > cc->timeline_out))) { + ((panel_timeline->trim_in_point && c->timeline_in < cc->timeline_in) || + (!panel_timeline->trim_in_point && c->timeline_out > cc->timeline_out))) { axis_ghosts[j] = c; found = true; break; @@ -814,7 +849,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { if (c != NULL && !panel_timeline->is_clip_selected(c)) { for (int j=0;jtrim_in) ? axis->timeline_in : axis->timeline_out; + long comp_point = (panel_timeline->trim_in_point) ? axis->timeline_in : axis->timeline_out; bool clip_is_pre = (c->timeline_in < comp_point); QVector& clip_list = clip_is_pre ? pre_clips : post_clips; bool found = false; @@ -834,10 +869,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { } } } -// qDebug() << "found" << pre_clips.size() << "pres and" << post_clips.size() << "posts"; - } - - init_ghosts(); + } panel_timeline->moving_proc = true; } @@ -925,7 +957,9 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { panel_timeline->rect_select_h = 0; panel_timeline->rect_select_proc = true; } - } else if (panel_timeline->tool == TIMELINE_TOOL_POINTER || panel_timeline->tool == TIMELINE_TOOL_RIPPLE) { + } else if (panel_timeline->tool == TIMELINE_TOOL_POINTER || + panel_timeline->tool == TIMELINE_TOOL_RIPPLE || + panel_timeline->tool == TIMELINE_TOOL_ROLLING) { track_resizing = false; QPoint pos = event->pos(); @@ -943,7 +977,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { int nc = qAbs(c->timeline_in + 1 - panel_timeline->cursor_frame); if (nc < closeness) { panel_timeline->trim_target = i; - panel_timeline->trim_in = true; + panel_timeline->trim_in_point = true; closeness = nc; found = true; } @@ -952,7 +986,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) { int nc = qAbs(c->timeline_out - 1 - panel_timeline->cursor_frame); if (nc < closeness) { panel_timeline->trim_target = i; - panel_timeline->trim_in = false; + panel_timeline->trim_in_point = false; closeness = nc; found = true; }