waveformview: implemented basic waveformview display
This commit is contained in:
@@ -45,6 +45,7 @@ private:
|
|||||||
|
|
||||||
class TimelineScaledWidget : public QWidget, public TimelineScaledObject
|
class TimelineScaledWidget : public QWidget, public TimelineScaledObject
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
TimelineScaledWidget(QWidget* parent = nullptr);
|
TimelineScaledWidget(QWidget* parent = nullptr);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,11 +29,10 @@
|
|||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QStyleOptionGraphicsItem>
|
#include <QStyleOptionGraphicsItem>
|
||||||
|
|
||||||
#include "audio/sumsamples.h"
|
|
||||||
#include "common/clamp.h"
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "node/block/transition/transition.h"
|
#include "node/block/transition/transition.h"
|
||||||
|
#include "widget/viewer/waveformview.h"
|
||||||
|
|
||||||
TimelineViewBlockItem::TimelineViewBlockItem(Block *block, QGraphicsItem* parent) :
|
TimelineViewBlockItem::TimelineViewBlockItem(Block *block, QGraphicsItem* parent) :
|
||||||
TimelineViewRect(parent),
|
TimelineViewRect(parent),
|
||||||
@@ -94,47 +93,12 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
|
|||||||
QByteArray w = wave_file.readAll();
|
QByteArray w = wave_file.readAll();
|
||||||
|
|
||||||
// FIXME: Hardcoded channel count
|
// FIXME: Hardcoded channel count
|
||||||
int channels = 2;
|
WaveformView::DrawWaveform(painter,
|
||||||
|
rect().toRect(),
|
||||||
const SampleSummer::Sum* samples = reinterpret_cast<const SampleSummer::Sum*>(w.constData());
|
this->GetScale(),
|
||||||
int nb_samples = w.size() / sizeof(SampleSummer::Sum);
|
reinterpret_cast<const SampleSummer::Sum*>(w.constData()),
|
||||||
|
w.size() / sizeof(SampleSummer::Sum),
|
||||||
int sample_index, next_sample_index = 0;
|
2);
|
||||||
|
|
||||||
QVector<SampleSummer::Sum> summary;
|
|
||||||
int summary_index = -1;
|
|
||||||
|
|
||||||
int channel_height = rect().height() / channels;
|
|
||||||
int channel_half_height = channel_height / 2;
|
|
||||||
|
|
||||||
for (int i=0;i<rect().width();i++) {
|
|
||||||
sample_index = next_sample_index;
|
|
||||||
|
|
||||||
if (sample_index == nb_samples) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_sample_index = qMin(nb_samples,
|
|
||||||
qFloor(static_cast<double>(SampleSummer::kSumSampleRate) * static_cast<double>(i+1) / this->GetScale()) * channels);
|
|
||||||
|
|
||||||
if (summary_index != sample_index) {
|
|
||||||
summary = SampleSummer::ReSumSamples(&samples[sample_index],
|
|
||||||
qMax(channels, next_sample_index - sample_index),
|
|
||||||
channels);
|
|
||||||
summary_index = sample_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
int line_x = i + rect().x();
|
|
||||||
|
|
||||||
for (int j=0;j<summary.size();j++) {
|
|
||||||
int channel_mid = rect().y() + channel_height * j + channel_half_height;
|
|
||||||
|
|
||||||
painter->drawLine(line_x,
|
|
||||||
channel_mid + clamp(qRound(summary.at(j).min * channel_half_height), -channel_half_height, channel_half_height),
|
|
||||||
line_x,
|
|
||||||
channel_mid + clamp(qRound(summary.at(j).max * channel_half_height), -channel_half_height, channel_half_height));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wave_file.close();
|
wave_file.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
|||||||
// Create scrollbar
|
// Create scrollbar
|
||||||
layout->addWidget(scrollbar());
|
layout->addWidget(scrollbar());
|
||||||
connect(scrollbar(), &QScrollBar::valueChanged, ruler(), &TimeRuler::SetScroll);
|
connect(scrollbar(), &QScrollBar::valueChanged, ruler(), &TimeRuler::SetScroll);
|
||||||
|
connect(scrollbar(), &QScrollBar::valueChanged, waveform_view_, &WaveformView::SetScroll);
|
||||||
|
|
||||||
// Create lower controls
|
// Create lower controls
|
||||||
controls_ = new PlaybackControls();
|
controls_ = new PlaybackControls();
|
||||||
@@ -94,6 +95,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
|||||||
connect(video_renderer_, &VideoRenderBackend::RangeInvalidated, ruler(), &TimeRuler::CacheInvalidatedRange);
|
connect(video_renderer_, &VideoRenderBackend::RangeInvalidated, ruler(), &TimeRuler::CacheInvalidatedRange);
|
||||||
audio_renderer_ = new AudioBackend(this);
|
audio_renderer_ = new AudioBackend(this);
|
||||||
|
|
||||||
|
waveform_view_->SetBackend(audio_renderer_);
|
||||||
|
|
||||||
connect(PixelFormat::instance(), &PixelFormat::FormatChanged, this, &ViewerWidget::UpdateRendererParameters);
|
connect(PixelFormat::instance(), &PixelFormat::FormatChanged, this, &ViewerWidget::UpdateRendererParameters);
|
||||||
|
|
||||||
SetAutoMaxScrollBar(true);
|
SetAutoMaxScrollBar(true);
|
||||||
@@ -180,6 +183,13 @@ void ViewerWidget::ConnectedNodeChanged(ViewerOutput *n)
|
|||||||
audio_renderer_->SetViewerNode(n);
|
audio_renderer_->SetViewerNode(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewerWidget::ScaleChangedEvent(const double &s)
|
||||||
|
{
|
||||||
|
TimeBasedWidget::ScaleChangedEvent(s);
|
||||||
|
|
||||||
|
waveform_view_->SetScale(s);
|
||||||
|
}
|
||||||
|
|
||||||
void ViewerWidget::resizeEvent(QResizeEvent *event)
|
void ViewerWidget::resizeEvent(QResizeEvent *event)
|
||||||
{
|
{
|
||||||
TimeBasedWidget::resizeEvent(event);
|
TimeBasedWidget::resizeEvent(event);
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ protected:
|
|||||||
virtual void DisconnectNodeInternal(ViewerOutput *) override;
|
virtual void DisconnectNodeInternal(ViewerOutput *) override;
|
||||||
virtual void ConnectedNodeChanged(ViewerOutput*n) override;
|
virtual void ConnectedNodeChanged(ViewerOutput*n) override;
|
||||||
|
|
||||||
|
virtual void ScaleChangedEvent(const double& s) override;
|
||||||
|
|
||||||
virtual void resizeEvent(QResizeEvent *event) override;
|
virtual void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
OpenGLBackend* video_renderer_;
|
OpenGLBackend* video_renderer_;
|
||||||
|
|||||||
@@ -1,24 +1,148 @@
|
|||||||
#include "waveformview.h"
|
#include "waveformview.h"
|
||||||
|
|
||||||
|
#include <QFile>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QtMath>
|
#include <QtMath>
|
||||||
|
|
||||||
|
#include "common/clamp.h"
|
||||||
|
|
||||||
WaveformView::WaveformView(QWidget *parent) :
|
WaveformView::WaveformView(QWidget *parent) :
|
||||||
TimeBasedWidget(parent)
|
TimelineScaledWidget(parent),
|
||||||
|
backend_(nullptr)
|
||||||
{
|
{
|
||||||
setAutoFillBackground(true);
|
setAutoFillBackground(true);
|
||||||
setStyleSheet("background: black;");
|
setBackgroundRole(QPalette::Base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformView::SetBackend(AudioRenderBackend *backend)
|
||||||
|
{
|
||||||
|
if (backend_) {
|
||||||
|
disconnect(backend_, &AudioRenderBackend::QueueComplete, this, static_cast<void (WaveformView::*)()>(&WaveformView::update));
|
||||||
|
}
|
||||||
|
|
||||||
|
backend_ = backend;
|
||||||
|
|
||||||
|
if (backend_) {
|
||||||
|
connect(backend_, &AudioRenderBackend::QueueComplete, this, static_cast<void (WaveformView::*)()>(&WaveformView::update));
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformView::DrawWaveform(QPainter *painter, const QRect& rect, const double& scale, const SampleSummer::Sum* samples, int nb_samples, int channels)
|
||||||
|
{
|
||||||
|
int sample_index, next_sample_index = 0;
|
||||||
|
|
||||||
|
QVector<SampleSummer::Sum> summary;
|
||||||
|
int summary_index = -1;
|
||||||
|
|
||||||
|
int channel_height = rect.height() / channels;
|
||||||
|
int channel_half_height = channel_height / 2;
|
||||||
|
|
||||||
|
for (int i=0;i<rect.width();i++) {
|
||||||
|
sample_index = next_sample_index;
|
||||||
|
|
||||||
|
if (sample_index == nb_samples) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_sample_index = qMin(nb_samples,
|
||||||
|
qFloor(static_cast<double>(SampleSummer::kSumSampleRate) * static_cast<double>(i+1) / scale) * channels);
|
||||||
|
|
||||||
|
if (summary_index != sample_index) {
|
||||||
|
summary = SampleSummer::ReSumSamples(&samples[sample_index],
|
||||||
|
qMax(channels, next_sample_index - sample_index),
|
||||||
|
channels);
|
||||||
|
summary_index = sample_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int line_x = i + rect.x();
|
||||||
|
|
||||||
|
for (int j=0;j<summary.size();j++) {
|
||||||
|
int channel_mid = rect.y() + channel_height * j + channel_half_height;
|
||||||
|
|
||||||
|
painter->drawLine(line_x,
|
||||||
|
channel_mid + clamp(qRound(summary.at(j).min * channel_half_height), -channel_half_height, channel_half_height),
|
||||||
|
line_x,
|
||||||
|
channel_mid + clamp(qRound(summary.at(j).max * channel_half_height), -channel_half_height, channel_half_height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaveformView::SetScroll(int scroll)
|
||||||
|
{
|
||||||
|
scroll_ = scroll;
|
||||||
|
|
||||||
|
qDebug() << "Got scroll" << scroll_;
|
||||||
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaveformView::paintEvent(QPaintEvent *event)
|
void WaveformView::paintEvent(QPaintEvent *event)
|
||||||
{
|
{
|
||||||
QWidget::paintEvent(event);
|
QWidget::paintEvent(event);
|
||||||
|
|
||||||
QPainter p(this);
|
if (!backend_ || backend_->CachePathName().isEmpty() || !backend_->params().is_valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
p.setPen(Qt::green);
|
const AudioRenderingParams& params = backend_->params();
|
||||||
|
|
||||||
|
QFile fs(backend_->CachePathName());
|
||||||
|
|
||||||
|
if (fs.open(QFile::ReadOnly)) {
|
||||||
|
|
||||||
|
QPainter p(this);
|
||||||
|
|
||||||
|
p.setPen(Qt::green);
|
||||||
|
|
||||||
|
int channel_height = height() / params.channel_count();
|
||||||
|
int channel_half_height = channel_height / 2;
|
||||||
|
|
||||||
|
int drew = 0;
|
||||||
|
|
||||||
|
fs.seek(params.samples_to_bytes(GetSampleIndexFromPixel(0)));
|
||||||
|
|
||||||
|
for (int x=0; x<width() && !fs.atEnd(); x++) {
|
||||||
|
|
||||||
|
int samples_len = GetSampleIndexFromPixel(x+1) - GetSampleIndexFromPixel(x);
|
||||||
|
int max_read_size = params.samples_to_bytes(samples_len);
|
||||||
|
|
||||||
|
QByteArray read_buffer = fs.read(max_read_size);
|
||||||
|
|
||||||
|
// Detect whether we've reached EOF and recalculate sample count if so
|
||||||
|
if (read_buffer.size() < max_read_size) {
|
||||||
|
samples_len = params.bytes_to_samples(read_buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<SampleSummer::Sum> samples = SampleSummer::SumSamples(reinterpret_cast<const float*>(read_buffer.constData()),
|
||||||
|
samples_len,
|
||||||
|
params.channel_count());
|
||||||
|
|
||||||
|
for (int i=0;i<params.channel_count();i++) {
|
||||||
|
int channel_mid = channel_height * i + channel_half_height;
|
||||||
|
|
||||||
|
p.drawLine(x,
|
||||||
|
channel_mid + samples.at(i).min * static_cast<float>(channel_half_height),
|
||||||
|
x,
|
||||||
|
channel_mid + samples.at(i).max * static_cast<float>(channel_half_height));
|
||||||
|
|
||||||
|
drew++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.close();
|
||||||
|
|
||||||
for (int i=0;i<width();i++) {
|
|
||||||
p.drawPoint(i, (height() / 2) + qSin(static_cast<double>(i) * 0.025) * (height() / 2));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WaveformView::ScaleChangedEvent(const double &)
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int WaveformView::GetSampleIndexFromPixel(int x) const
|
||||||
|
{
|
||||||
|
qDebug() << "X was" << x << "scroll was" << scroll_ << "=" << (x + scroll_);
|
||||||
|
return qRound(static_cast<double>(x + scroll_) * static_cast<double>(backend_->params().sample_rate()) / GetScale());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,40 @@
|
|||||||
#ifndef WAVEFORMVIEW_H
|
#ifndef WAVEFORMVIEW_H
|
||||||
#define WAVEFORMVIEW_H
|
#define WAVEFORMVIEW_H
|
||||||
|
|
||||||
#include "widget/timebased/timebased.h"
|
#include <QWidget>
|
||||||
|
|
||||||
class WaveformView : public TimeBasedWidget
|
#include "audio/sumsamples.h"
|
||||||
|
#include "render/audioparams.h"
|
||||||
|
#include "render/backend/audiorenderbackend.h"
|
||||||
|
#include "widget/timelinewidget/timelinescaledobject.h"
|
||||||
|
|
||||||
|
class WaveformView : public TimelineScaledWidget
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
WaveformView(QWidget* parent = nullptr);
|
WaveformView(QWidget* parent = nullptr);
|
||||||
|
|
||||||
|
//void SetData(const QString& file, const AudioRenderingParams& params);
|
||||||
|
|
||||||
|
void SetBackend(AudioRenderBackend* backend);
|
||||||
|
|
||||||
|
static void DrawWaveform(QPainter* painter, const QRect &rect, const double &scale, const SampleSummer::Sum *samples, int nb_samples, int channels);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void SetScroll(int scroll);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void paintEvent(QPaintEvent* event) override;
|
virtual void paintEvent(QPaintEvent* event) override;
|
||||||
|
|
||||||
|
virtual void ScaleChangedEvent(const double& s) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int GetSampleIndexFromPixel(int x) const;
|
||||||
|
|
||||||
|
AudioRenderBackend* backend_;
|
||||||
|
|
||||||
|
int scroll_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WAVEFORMVIEW_H
|
#endif // WAVEFORMVIEW_H
|
||||||
|
|||||||
Reference in New Issue
Block a user