added waveform support in footage viewer and fixed audio regression where sound stopped playing if it hit EOF
This commit is contained in:
+36
-38
@@ -1777,7 +1777,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *event) {
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::leaveEvent(QEvent *event) {
|
||||
void TimelineWidget::leaveEvent(QEvent*) {
|
||||
tooltip_timer.stop();
|
||||
}
|
||||
|
||||
@@ -1785,6 +1785,37 @@ int color_brightness(int r, int g, int b) {
|
||||
return (0.2126*r + 0.7152*g + 0.0722*b);
|
||||
}
|
||||
|
||||
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter *p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom) {
|
||||
int divider = ms->audio_channels*2;
|
||||
int channel_height = clip_rect.height()/ms->audio_channels;
|
||||
|
||||
for (int i=waveform_start;i<waveform_limit;i++) {
|
||||
int waveform_index = qFloor((((clip->clip_in + ((double) i/zoom))/media_length) * ms->audio_preview.size())/divider)*divider;
|
||||
|
||||
if (clip->reverse) {
|
||||
waveform_index = ms->audio_preview.size() - waveform_index - (ms->audio_channels * 2);
|
||||
}
|
||||
|
||||
for (int j=0;j<ms->audio_channels;j++) {
|
||||
int mid = (config.rectified_waveforms) ? clip_rect.top()+channel_height*(j+1) : clip_rect.top()+channel_height*j+(channel_height/2);
|
||||
int offset = waveform_index+(j*2);
|
||||
|
||||
if ((offset + 1) < ms->audio_preview.size()) {
|
||||
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
|
||||
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
|
||||
|
||||
if (config.rectified_waveforms) {
|
||||
p->drawLine(clip_rect.left()+i, mid, clip_rect.left()+i, mid - (max - min));
|
||||
} else {
|
||||
p->drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
}
|
||||
}/* else {
|
||||
qDebug() << "[WARNING] Tried to reach" << offset + 1 << ", limit:" << ms->audio_preview.size();
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
// Draw clips
|
||||
if (sequence != NULL) {
|
||||
@@ -1869,6 +1900,9 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
}
|
||||
}
|
||||
} else if (clip_rect.height() > TRACK_MIN_HEIGHT) {
|
||||
// draw waveform
|
||||
p.setPen(QColor(80, 80, 80));
|
||||
|
||||
int waveform_start = -qMin(clip_rect.x(), 0);
|
||||
int waveform_limit = qMin(clip_rect.width(), getScreenPointFromFrame(panel_timeline->zoom, media_length - clip->clip_in));
|
||||
|
||||
@@ -1879,43 +1913,7 @@ void TimelineWidget::paintEvent(QPaintEvent*) {
|
||||
if (waveform_limit > 0) checkerboard_rect.setLeft(checkerboard_rect.left() + waveform_limit);
|
||||
}
|
||||
|
||||
// draw waveform
|
||||
p.setPen(QColor(80, 80, 80));
|
||||
|
||||
int divider = ms->audio_channels*2;
|
||||
int channel_height = clip_rect.height()/ms->audio_channels;
|
||||
|
||||
for (int i=waveform_start;i<waveform_limit;i++) {
|
||||
int waveform_index = qFloor((((clip->clip_in + ((double) i/panel_timeline->zoom))/media_length) * ms->audio_preview.size())/divider)*divider;
|
||||
|
||||
if (clip->reverse) {
|
||||
waveform_index = ms->audio_preview.size() - waveform_index - (ms->audio_channels * 2);
|
||||
}
|
||||
|
||||
int rectified_height = 0;
|
||||
|
||||
for (int j=0;j<ms->audio_channels;j++) {
|
||||
int mid = clip_rect.top()+channel_height*j+(channel_height/2);
|
||||
int offset = waveform_index+(j*2);
|
||||
|
||||
if ((offset + 1) < ms->audio_preview.size()) {
|
||||
qint8 min = (double)ms->audio_preview.at(offset) / 128.0 * (channel_height/2);
|
||||
qint8 max = (double)ms->audio_preview.at(offset+1) / 128.0 * (channel_height/2);
|
||||
|
||||
if (config.rectified_waveforms) {
|
||||
rectified_height += (max - min);
|
||||
} else {
|
||||
p.drawLine(clip_rect.left()+i, mid+min, clip_rect.left()+i, mid+max);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "[WARNING] Tried to reach" << offset + 1 << ", limit:" << ms->audio_preview.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (config.rectified_waveforms) {
|
||||
p.drawLine(clip_rect.left()+i, clip_rect.height(), clip_rect.left()+i, clip_rect.height() - rectified_height);
|
||||
}
|
||||
}
|
||||
draw_waveform(clip, ms, media_length, &p, clip_rect, waveform_start, waveform_limit, panel_timeline->zoom);
|
||||
}
|
||||
}
|
||||
if (draw_checkerboard) {
|
||||
|
||||
@@ -13,12 +13,15 @@
|
||||
|
||||
struct Sequence;
|
||||
struct Clip;
|
||||
struct MediaStream;
|
||||
class Timeline;
|
||||
class TimelineAction;
|
||||
class QScrollBar;
|
||||
class SetSelectionsCommand;
|
||||
class QPainter;
|
||||
|
||||
bool same_sign(int a, int b);
|
||||
void draw_waveform(Clip* clip, MediaStream* ms, long media_length, QPainter* p, const QRect& clip_rect, int waveform_start, int waveform_limit, double zoom);
|
||||
|
||||
class TimelineWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
#include <QResizeEvent>
|
||||
|
||||
// enforces aspect ratio
|
||||
ViewerContainer::ViewerContainer(QWidget *parent) : QWidget(parent) {
|
||||
child = NULL;
|
||||
aspect_ratio = 1;
|
||||
}
|
||||
ViewerContainer::ViewerContainer(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
child(NULL),
|
||||
aspect_ratio(1)
|
||||
{}
|
||||
|
||||
void ViewerContainer::adjust() {
|
||||
if (child != NULL) {
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
void adjust();
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
void resizeEvent(QResizeEvent *event);
|
||||
|
||||
signals:
|
||||
|
||||
|
||||
+80
-54
@@ -27,10 +27,12 @@ extern "C" {
|
||||
}
|
||||
|
||||
ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
QOpenGLWidget(parent),
|
||||
rendering(false),
|
||||
default_fbo(NULL)
|
||||
QOpenGLWidget(parent),
|
||||
default_fbo(NULL),
|
||||
waveform(false)
|
||||
{
|
||||
setFocusPolicy(Qt::ClickFocus);
|
||||
|
||||
QSurfaceFormat format;
|
||||
format.setDepthBufferSize(24);
|
||||
setFormat(format);
|
||||
@@ -175,62 +177,71 @@ GLuint ViewerWidget::compose_sequence(Clip* nest, bool render_audio) {
|
||||
|
||||
QVector<Clip*> current_clips;
|
||||
|
||||
long endFrame = LONG_MIN;
|
||||
for (int i=0;i<s->clips.size();i++) {
|
||||
Clip* c = s->clips.at(i);
|
||||
|
||||
// if clip starts within one second and/or hasn't finished yet
|
||||
if (c != NULL && !(nest != NULL && !same_sign(c->track, nest->track))) {
|
||||
bool clip_is_active = false;
|
||||
if (c != NULL) {
|
||||
endFrame = qMax(endFrame, c->timeline_out);
|
||||
|
||||
switch (c->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Media* m = static_cast<Media*>(c->media);
|
||||
if (m->ready) {
|
||||
MediaStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms != NULL && is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, !rendering);
|
||||
if (!(nest != NULL && !same_sign(c->track, nest->track))) {
|
||||
bool clip_is_active = false;
|
||||
|
||||
switch (c->media_type) {
|
||||
case MEDIA_TYPE_FOOTAGE:
|
||||
{
|
||||
Media* m = static_cast<Media*>(c->media);
|
||||
if (m->ready) {
|
||||
MediaStream* ms = m->get_stream_from_file_index(c->track < 0, c->media_stream);
|
||||
if (ms != NULL && is_clip_active(c, playhead)) {
|
||||
// if thread is already working, we don't want to touch this,
|
||||
// but we also don't want to hang the UI thread
|
||||
if (!c->open) {
|
||||
open_clip(c, !rendering);
|
||||
}
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
} else {
|
||||
texture_failed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
case MEDIA_TYPE_SOLID:
|
||||
case MEDIA_TYPE_TONE:
|
||||
if (is_clip_active(c, playhead)) {
|
||||
if (!c->open) open_clip(c, !rendering);
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
} else {
|
||||
texture_failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MEDIA_TYPE_SEQUENCE:
|
||||
case MEDIA_TYPE_SOLID:
|
||||
case MEDIA_TYPE_TONE:
|
||||
if (is_clip_active(c, playhead)) {
|
||||
if (!c->open) open_clip(c, !rendering);
|
||||
clip_is_active = true;
|
||||
} else if (c->open) {
|
||||
close_clip(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (clip_is_active) {
|
||||
bool added = false;
|
||||
for (int j=0;j<current_clips.size();j++) {
|
||||
if (current_clips.at(j)->track < c->track) {
|
||||
current_clips.insert(j, c);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
current_clips.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clip_is_active) {
|
||||
bool added = false;
|
||||
for (int j=0;j<current_clips.size();j++) {
|
||||
if (current_clips.at(j)->track < c->track) {
|
||||
current_clips.insert(j, c);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
current_clips.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewer->playing && s->playhead == endFrame) {
|
||||
viewer->pause();
|
||||
}
|
||||
|
||||
int half_width = s->width/2;
|
||||
int half_height = s->height/2;
|
||||
if (rendering || nest != NULL) half_height = -half_height;
|
||||
@@ -444,28 +455,43 @@ void ViewerWidget::paintGL() {
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
texture_failed = false;
|
||||
texture_failed = false;
|
||||
|
||||
if (!rendering) retry_timer.stop();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// compose video preview
|
||||
glClearColor(0, 0, 0, 0);
|
||||
compose_sequence(NULL, (viewer->playing || rendering));
|
||||
|
||||
if (texture_failed) {
|
||||
if (waveform) {
|
||||
double waveform_zoom = (double) waveform_ms->audio_preview.size() / (double) width();
|
||||
double timeline_zoom = (double) width() / (double) waveform_clip->timeline_out;
|
||||
|
||||
QPainter p(this);
|
||||
p.setPen(QColor(0, 255, 0));
|
||||
if (viewer->seq->using_workarea) {
|
||||
p.fillRect(QRect(getScreenPointFromFrame(timeline_zoom, viewer->seq->workarea_in), 0, getScreenPointFromFrame(timeline_zoom, viewer->seq->workarea_out-viewer->seq->workarea_in), height()), QColor(255, 255, 255, 64));
|
||||
}
|
||||
draw_waveform(waveform_clip, waveform_ms, waveform_clip->timeline_out, &p, rect(), 0, width(), waveform_zoom);
|
||||
p.setPen(Qt::red);
|
||||
int playhead_x = getScreenPointFromFrame(timeline_zoom, viewer->seq->playhead);
|
||||
p.drawLine(playhead_x, 0, playhead_x, height());
|
||||
}
|
||||
|
||||
if (texture_failed) {
|
||||
if (rendering) {
|
||||
qDebug() << "[INFO] Texture failed - looping";
|
||||
loop = true;
|
||||
} else {
|
||||
} else {
|
||||
retry_timer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.show_title_safe_area && !rendering) {
|
||||
drawTitleSafeArea();
|
||||
}
|
||||
if (config.show_title_safe_area && !rendering) {
|
||||
drawTitleSafeArea();
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
+5
-1
@@ -11,6 +11,7 @@
|
||||
|
||||
class Viewer;
|
||||
struct Clip;
|
||||
struct MediaStream;
|
||||
class QOpenGLFramebufferObject;
|
||||
|
||||
class ViewerWidget : public QOpenGLWidget
|
||||
@@ -19,11 +20,14 @@ class ViewerWidget : public QOpenGLWidget
|
||||
public:
|
||||
ViewerWidget(QWidget *parent = 0);
|
||||
|
||||
bool rendering;
|
||||
void paintGL();
|
||||
void initializeGL();
|
||||
Viewer* viewer;
|
||||
|
||||
bool waveform;
|
||||
Clip* waveform_clip;
|
||||
MediaStream* waveform_ms;
|
||||
|
||||
QOpenGLFramebufferObject* default_fbo;
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e);
|
||||
|
||||
Reference in New Issue
Block a user