diff --git a/src/Lib/AbstractMediaContext.hh b/src/Lib/AbstractMediaContext.hh index a15609de50345b89f238deae99cbb8722de14456..089b4db2da8b72b5b3c397079ddf6f757787add8 100644 --- a/src/Lib/AbstractMediaContext.hh +++ b/src/Lib/AbstractMediaContext.hh @@ -99,6 +99,13 @@ public: return ret; } + qreal getDuration() const noexcept + { + TODO(Use std::chrono here) + // The whole file duration, not individual streams. + return static_cast<qreal>(dataFormat->duration) / 1000000; + } + protected: // Codec related informations AVCodecID codecId{ AV_CODEC_ID_NONE }; @@ -185,6 +192,13 @@ public: return StreamWeakPtr{ spareNullSreamPtr }; } + qreal getLength() const noexcept + { + TODO(Use std::chrono here) + // The whole file duration, not individual streams. + return static_cast<qreal>(format->duration) / 1000000; + } + StreamWeakPtr getDefaultStream() const noexcept { return (defaultStreamIndex < 0) ? StreamWeakPtr{ spareNullSreamPtr } diff --git a/src/Lib/Video.cc b/src/Lib/Video.cc index e6b9a07022e0e77f352b5a90e664ea2111c9d882..72d894f4b230bd4e616e39bb85f11bbeddafe570 100644 --- a/src/Lib/Video.cc +++ b/src/Lib/Video.cc @@ -44,12 +44,6 @@ VideoStream::getHeight() const noexcept return codecContext->height; } -qint64 -VideoStream::getDuration() const noexcept -{ - return dataFormat->duration; -} - double VideoStream::getFramesPerSecond() const noexcept { diff --git a/src/Lib/Video.hh b/src/Lib/Video.hh index 400feb30480d89cae9741a98c5cd9b2d7edabe2d..4092908f7518284eae122a64eb950d608daff14f 100644 --- a/src/Lib/Video.hh +++ b/src/Lib/Video.hh @@ -19,7 +19,6 @@ public: int getWidth() const noexcept; int getHeight() const noexcept; - qint64 getDuration() const noexcept; double getFramesPerSecond() const noexcept; QJsonObject getProperties() const noexcept override; diff --git a/src/UI/DocumentViews/AudioVisualizer.cc b/src/UI/DocumentViews/AudioVisualizer.cc index bab53a25f3755ba476fc24e7723e3dd70c699d6f..30970a2a9fec7df867b17cd0ab27e257081dde98 100644 --- a/src/UI/DocumentViews/AudioVisualizer.cc +++ b/src/UI/DocumentViews/AudioVisualizer.cc @@ -59,13 +59,13 @@ AudioVisualizer::AudioVisualizer(AudioContext::StreamPtr stream, QWidget *parent QImage img = QImage(pixels, static_cast<int>(width), static_cast<int>(height / 2), static_cast<int>(width), QImage::Format_Grayscale8, pixelsDeleter, pixels) .mirrored(false, true); - printSpectrum(img); + printSpectrum(img, stream->getDuration()); } void -AudioVisualizer::printSpectrum(QImage pixmap) noexcept +AudioVisualizer::printSpectrum(QImage pixmap, qreal audioLength) noexcept { - TimingView *timer = new TimingView(pixmap, 0, this); + TimingView *timer = new TimingView(pixmap, audioLength, this); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(timer); diff --git a/src/UI/DocumentViews/AudioVisualizer.hh b/src/UI/DocumentViews/AudioVisualizer.hh index 47032201caaf6ef875e0555c8c46205542c44150..a69436677b049e297ef85dbf041eda8fa0eb6916 100644 --- a/src/UI/DocumentViews/AudioVisualizer.hh +++ b/src/UI/DocumentViews/AudioVisualizer.hh @@ -34,7 +34,7 @@ public: ~AudioVisualizer() noexcept override = default; public slots: - void printSpectrum(QImage) noexcept; + void printSpectrum(QImage, qreal) noexcept; }; } diff --git a/src/UI/DocumentViews/TimingAxis.cc b/src/UI/DocumentViews/TimingAxis.cc new file mode 100644 index 0000000000000000000000000000000000000000..2d7f6c7370b72c71724a9b8cb003f2c728e5f5a5 --- /dev/null +++ b/src/UI/DocumentViews/TimingAxis.cc @@ -0,0 +1,65 @@ +#include "TimingAxis.hh" + +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QLabel> +#include <QMessageBox> +#include <QMouseEvent> +#include <QPen> +#include <QScrollArea> +#include <QScrollBar> +#include <QVBoxLayout> + +using namespace Vivy; + +TimingAxis::TimingAxis(qreal soundLength_, int x0_, int x1_, int y_) noexcept + : QGraphicsItem() + , soundLength(soundLength_) + , x0(x0_) + , x1(x1_) + , y(y_) +{ +} + +QRectF +TimingAxis::boundingRect() const +{ + return QRectF(x0, y - penWidth, x1, y + penWidth); +} + +void +TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + painter->drawLine(x0, y, x1, y); + + if (soundLength == 0) + return; + + qDebug() << "Audio length is " << soundLength; + int minorTicksIndex = 0; + // Find what is our ticks + QGraphicsScene *currScene = scene(); + if (currScene != nullptr) { + for (minorTicksIndex = 0; minorTicksIndex < availableTicks.size(); minorTicksIndex++) { + qDebug() << "Calculated is " + << currScene->width() / (qreal(soundLength) * availableTicks[minorTicksIndex]); + + if (currScene->width() / (qreal(soundLength) * availableTicks[minorTicksIndex]) < + minBetweenMinor) { + minorTicksIndex = minorTicksIndex == 0 ? 0 : minorTicksIndex - 1; + break; + } + } + } + qreal minorTicks = availableTicks[minorTicksIndex]; + qreal majorTicks = availableTicks[minorTicksIndex + 1]; + + qDebug() << "Minor ticks : " << minorTicks; + qDebug() << "Major ticks : " << majorTicks; + + for (qreal i = majorTicks; i < x1 - x0; i += majorTicks) + painter->drawLine(int(x0 + i), y - 5, int(x0 + i), y + 5); + for (qreal i = minorTicks; i < x1 - x0; i += minorTicks) + if (fmod(i, majorTicks)) + painter->drawLine(int(x0 + i), y - 2, int(x0 + i), y + 2); +} diff --git a/src/UI/DocumentViews/TimingAxis.hh b/src/UI/DocumentViews/TimingAxis.hh new file mode 100644 index 0000000000000000000000000000000000000000..0ed6ea2e98ebb8205aea0436bdd0f474a7065825 --- /dev/null +++ b/src/UI/DocumentViews/TimingAxis.hh @@ -0,0 +1,39 @@ +#ifndef VIVY_TIMING_AXIS_H +#define VIVY_TIMING_AXIS_H + +#ifndef __cplusplus +#error "This is a C++ header" +#endif + +#include <QGraphicsWidget> +#include <QPainter> +#include <cmath> + +namespace Vivy +{ +class TimingAxis final : public QGraphicsItem { +public: + explicit TimingAxis(qreal soundLength, int x0, int x1, int y) noexcept; + ~TimingAxis() noexcept = default; + + QRectF boundingRect() const override; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; + +private: + static inline constexpr QColor axisColour = QColor(0, 0, 127); + + QVector<qreal> availableTicks = { 0.01, 0.1, 1, 10, 60, 3600, 86400 }; + qreal minBetweenMinor = 5; + + qreal penWidth = { 1 }; + qreal soundLength = { 0 }; + int x0 = { 0 }; + int x1 = { 0 }; + int y = { 0 }; + +protected: +}; + +} + +#endif // VIVY_TIMING_AXIS_H diff --git a/src/UI/DocumentViews/TimingScene.cc b/src/UI/DocumentViews/TimingScene.cc index e9db17e38c240717c21e814c98139e7585a80f16..92fcf7a93e3a0411fdfd530c5c75a170aa7f62d3 100644 --- a/src/UI/DocumentViews/TimingScene.cc +++ b/src/UI/DocumentViews/TimingScene.cc @@ -20,13 +20,15 @@ TimingScene::TimingScene(QWidget *parent) noexcept { } -TimingScene::TimingScene(QImage img_, quint64 soundLength_, QWidget *parent) noexcept +TimingScene::TimingScene(QImage img_, qreal soundLength_, QWidget *parent) noexcept : QGraphicsScene(parent) , img(img_) , soundLength(soundLength_) { QPixmap pixmap(QPixmap::fromImage(img)); - backgroundImg = addPixmap(pixmap); + backgroundImg = addPixmap(pixmap); + TimingAxis *ax = new TimingAxis(soundLength, 0, pixmap.width(), 10); + addItem(ax); } void @@ -36,7 +38,7 @@ TimingScene::mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept QGraphicsItem *got = itemAt(pos, QTransform()); Ass::LinePtr p = currentLine.lock(); - if (p && (got == nullptr || got == backgroundImg)) [[likely]] { + if (p && (got == nullptr || got == backgroundImg)) { // Handle the different cases if (timingMode == TimingMode::Line) handleMousePressEventLine(event, p); @@ -53,7 +55,7 @@ void TimingScene::handleMousePressEventLine(QGraphicsSceneMouseEvent *event, Ass::LinePtr p) noexcept { QPointF pos = event->scenePos(); - quint64 time = timeFromPos(pos.x()); + quint64 time = static_cast<quint64>(timeFromPos(pos.x())); if (const auto &btn = event->button(); btn == Qt::LeftButton) { p->setStart(time); @@ -72,14 +74,14 @@ TimingScene::handleMousePressEventChar(QGraphicsSceneMouseEvent *, Ass::LinePtr) { } -quint64 +qreal TimingScene::timeFromPos(qreal x) { if (const qreal w = width(); x <= 0 || w <= 0) { qCritical() << "Try avoid possible divide by zero in the time from position"; return 0; } else { - return static_cast<quint64>(x) * soundLength / static_cast<quint64>(w); + return x * soundLength / w; } } diff --git a/src/UI/DocumentViews/TimingScene.hh b/src/UI/DocumentViews/TimingScene.hh index cb81a141d1e83c61d7f24fab55e140e956c2eacb..31f4b5484d19f970863c2dd6cdbaf719cfb3b0a2 100644 --- a/src/UI/DocumentViews/TimingScene.hh +++ b/src/UI/DocumentViews/TimingScene.hh @@ -3,6 +3,7 @@ #include "../../Lib/Utils.hh" #include "../../Lib/Ass/Ass.hh" #include "TimingBar.hh" +#include "TimingAxis.hh" namespace Vivy { @@ -18,12 +19,13 @@ public: static inline constexpr QColor endColour = QColor(0, 127, 0); explicit TimingScene(QWidget *parent = nullptr) noexcept; - explicit TimingScene(QImage, quint64, QWidget * = nullptr) noexcept; + explicit TimingScene(QImage, qreal, QWidget * = nullptr) noexcept; + ~TimingScene() noexcept override = default; private: QGraphicsPixmapItem *backgroundImg{ nullptr }; QImage img; - quint64 soundLength{ 0 }; + qreal soundLength{ 0 }; Ass::LineWeakPtr currentLine{}; TimingMode timingMode{ TimingMode::Line }; @@ -32,10 +34,10 @@ public: void mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept override; private: - quint64 timeFromPos(qreal x); void handleMousePressEventLine(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; void handleMousePressEventSyl(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; void handleMousePressEventChar(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; + qreal timeFromPos(qreal x); public slots: }; diff --git a/src/UI/DocumentViews/TimingView.cc b/src/UI/DocumentViews/TimingView.cc index c5391b1611aff4110d1a73336206bf10c4f3a680..8da43146f31237066143e87c7516cc61f26b8ae0 100644 --- a/src/UI/DocumentViews/TimingView.cc +++ b/src/UI/DocumentViews/TimingView.cc @@ -2,7 +2,7 @@ using namespace Vivy; -TimingView::TimingView(QImage img, quint64 soundLength, QWidget *parent) noexcept +TimingView::TimingView(QImage img, qreal soundLength, QWidget *parent) noexcept : QGraphicsView(parent) { scene = new TimingScene(img, soundLength, this); @@ -15,6 +15,7 @@ TimingView::TimingView(QImage img, quint64 soundLength, QWidget *parent) noexcep &TimingView::moveScrollBarToBottom); verticalScrollBar()->setValue(verticalScrollBar()->maximum()); horizontalScrollBar()->setValue(horizontalScrollBar()->minimum()); + setScene(scene); } diff --git a/src/UI/DocumentViews/TimingView.hh b/src/UI/DocumentViews/TimingView.hh index 0d68b4fa9f09d3e3fe9a42b8c5364894153ad25b..4c878ec6c62c2e491b440f2d344529118677626e 100644 --- a/src/UI/DocumentViews/TimingView.hh +++ b/src/UI/DocumentViews/TimingView.hh @@ -7,6 +7,7 @@ #include "../../Lib/Utils.hh" #include "TimingBar.hh" #include "TimingScene.hh" +#include "TimingAxis.hh" namespace Vivy { @@ -19,7 +20,7 @@ public: static inline constexpr QColor startColour = QColor(127, 0, 127); static inline constexpr QColor endColour = QColor(0, 127, 0); - explicit TimingView(QImage, quint64, QWidget * = nullptr) noexcept; + explicit TimingView(QImage, qreal, QWidget * = nullptr) noexcept; ~TimingView() noexcept override = default; void wheelEvent(QWheelEvent *) noexcept override;