diff --git a/src/Lib/Audio.cc b/src/Lib/Audio.cc index 96f5c4f0b69c8282f6b5fb126d00305ee23bf4be..15521c875b42191e229dbf5648974bf9553a0247 100644 --- a/src/Lib/Audio.cc +++ b/src/Lib/Audio.cc @@ -127,7 +127,7 @@ AudioStream::decodeData() const size_t frame_count = static_cast<size_t>(frame_count_int); dataPtr = reinterpret_cast<double *>( realloc(dataPtr, (dataSize + static_cast<size_t>(dataFrame->nb_samples)) * - sizeof(double))); + sizeof(double))); memcpy(dataPtr + dataSize, buffer, frame_count * sizeof(double)); dataSize += frame_count; } @@ -217,9 +217,10 @@ AudioStream::getDecodedDecalage() const noexcept return getDecodedChunkSize() - overlap; } - quint64 AudioStream::getLength() const noexcept { - return quint64(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::microseconds(dataFormat->duration)).count()); + return quint64(std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::microseconds(dataFormat->duration)) + .count()); } diff --git a/src/UI/DocumentViews/AudioVisualizer.cc b/src/UI/DocumentViews/AudioVisualizer.cc index 1ce4b217ee353b8c9c5bfe6374c717f7160aefd5..3a24341575ad12804254e2181ece722297017d20 100644 --- a/src/UI/DocumentViews/AudioVisualizer.cc +++ b/src/UI/DocumentViews/AudioVisualizer.cc @@ -67,17 +67,18 @@ AudioVisualizer::AudioVisualizer(AudioContext::StreamPtr stream, QWidget *parent void AudioVisualizer::printSpectrum(QImage pixmap, AudioContext::StreamPtr stream) noexcept { - TimingView *timer = new TimingView(pixmap, stream, this); - TimingParams *params= new TimingParams(this); + TimingScene *timingScene = new TimingScene(pixmap, stream, this); + TimingView *timingView = new TimingView(timingScene, pixmap, stream, this); + TimingParams *params = new TimingParams(this); - // The only that we want to take all the space is the timer in itself + // The only that we want to take all the space is the timing scene in itself QGridLayout *layout = new QGridLayout; - layout->addWidget(timer, 1, 0); + layout->addWidget(timingView, 1, 0); layout->setColumnStretch(0, 10); layout->addWidget(params, 1, 1); layout->setColumnStretch(1, 0); setLayout(layout); - connect(params->getZoomSlider(), &QSlider::valueChanged, timer->getTimingScene()->getAxis(), + connect(params->getZoomSlider(), &QSlider::valueChanged, timingScene->getAxis(), &TimingAxis::refreshTicks); } diff --git a/src/UI/DocumentViews/AudioVisualizer.hh b/src/UI/DocumentViews/AudioVisualizer.hh index 25022705f3369542e70501698bd64beb479ac6af..a8d7c55aa09fe353f3bfbe236750d3e4ac96fb0b 100644 --- a/src/UI/DocumentViews/AudioVisualizer.hh +++ b/src/UI/DocumentViews/AudioVisualizer.hh @@ -5,6 +5,7 @@ #error "This is a C++ header" #endif +#include "AudioVisualizer/TimingScene.hh" #include "AudioVisualizer/TimingView.hh" #include "AudioVisualizer/TimingParams.hh" #include "../../Lib/Audio.hh" diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc index 7183b82614d46b08ad7fc57d9f16db4e2721df62..5f41f1688a6d87c4c48a68b27961b28dc932b553 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc @@ -10,22 +10,25 @@ #include <QScrollBar> #include <QVBoxLayout> #include <QTime> +#include <QStyleOptionGraphicsItem> + +#include "TimingUtils.hh" using namespace Vivy; -TimingAxis::TimingAxis(AudioContext::StreamPtr stream, int x0_, int x1_, int y_) noexcept - : QGraphicsObject(), - audioStream(stream), - x0(x0_), - x1(x1_), - y(y_) +TimingAxis::TimingAxis(AudioContext::StreamPtr stream) noexcept + : QGraphicsObject() + , audioStream(stream) { + setPos(0, height); + pen = QPen(axisColour); + pen.setWidth(penWidth); } QRectF TimingAxis::boundingRect() const { - return QRectF(x0, 0, x1, y + ticksBase + penWidth*2); + return QRectF(0, penWidth, scene()->width(), -height - penWidth); } /* @@ -39,11 +42,11 @@ TimingAxis::refreshTicks() int nbAvailableTicks = availableTicks.size(); QGraphicsScene *parentScene = scene(); int minorTicksIndex = 0; - quint64 length { audioStream->getLength() }; - quint64 width { quint64(scene()->width()) }; - if (parentScene != nullptr){ - for (minorTicksIndex = 0; minorTicksIndex < nbAvailableTicks; minorTicksIndex++){ - if (width * quint64(availableTicks[minorTicksIndex]) / length >= minBetweenMinor){ + int length{ int(audioStream->getLength()) }; + int width{ int(parentScene->width()) }; + if (parentScene != nullptr) { + for (minorTicksIndex = 0; minorTicksIndex < nbAvailableTicks; minorTicksIndex++) { + if (width * availableTicks[minorTicksIndex] / length >= minBetweenMinor) { break; } } @@ -56,49 +59,53 @@ TimingAxis::refreshTicks() minorTicks = 0; majorTicks = availableTicks[minorTicksIndex]; } + update(); } -void TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - if (audioStream->getLength() == 0) +void +TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + int audioLength; + if ((audioLength = int(audioStream->getLength())) == 0) return; - qDebug() << "Painting axis..."; - int ticks_base = y+ticksBase; - int yText = ticks_base - majorTicksHeight - timeDigitsMargin; - int majorTicksUp = ticks_base - majorTicksHeight; - int minorTicksUp = ticks_base - minorTicksHeight; - quint64 length { audioStream->getLength() }; - quint64 width { quint64(scene()->width()) }; + int yText = -majorTicksHeight - timeDigitsMargin; + int width{ int(scene()->width()) }; - painter->drawLine(x0, ticks_base, x1, ticks_base); + painter->setPen(pen); + painter->drawLine(0, 0, audioLength, 0); - for (quint64 i = 0; i < length; i += majorTicks){ - int pos = x0 + int(posFromMs(i, width, length)); + /* + * We may want to calculate width/audioLength + * and audioLength/width to have just a multiplication + * to do to get the position of each tick + * + * It should be faster at the cost of precision : + * should look whether this precision loss is negligible or not + */ + for (int i = 0; i < audioLength; i += majorTicks) { + int pos = TimingUtils::posFromMs(i, width, audioLength); painter->drawText(QPoint(pos, yText), msToString(i)); - painter->drawLine(int(pos), majorTicksUp, int(pos), ticks_base); + painter->drawLine(pos, 0, pos, -majorTicksHeight); } - if (minorTicks > 0){ - for (quint64 i = 0; i < length; i += minorTicks){ - quint64 pos = quint64(x0) + posFromMs(i, width, length); - if (fmod(i, majorTicks) != 0) - painter->drawLine(int(pos), minorTicksUp, int(pos), ticks_base); + if (minorTicks > 0) { + for (int i = 0; i < audioLength; i += minorTicks) { + int pos = TimingUtils::posFromMs(i, width, audioLength); + if (Q_LIKELY(fmod(i, majorTicks) != 0)) + painter->drawLine(pos, 0, pos, -minorTicksHeight); } } } QString -TimingAxis::msToString(quint64 i) const noexcept +TimingAxis::msToString(int i) const noexcept { - QString ret(QStringLiteral("%1").arg(i % 60000 / 1000, i>= 60000 ? 2 : 1, 10, QLatin1Char('0')) + QString(".") + QString::number(i % 1000 / majorTicks)); + QString ret( + QStringLiteral("%1").arg(i % 60000 / 1000, i >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + + QString(".") + QString::number(i % 1000 / majorTicks)); if (i >= 60000) - ret.prepend(QString::number(i/60000) + QString(":")); + ret.prepend(QString::number(i / 60000) + QString(":")); if (i >= 360000) - ret.prepend(QString::number(i/360000) + QString(":")); + ret.prepend(QString::number(i / 360000) + QString(":")); return ret; } - -inline quint64 -TimingAxis::posFromMs(quint64 t, quint64 width, quint64 length) const noexcept -{ - return t * width / length; -} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh index 4180a9e364fa1b80a5b5f5b118e643164bdfb4a4..6359e7e620131e9ef312c57366e8b5034e38694c 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh @@ -9,6 +9,9 @@ #include <QPainter> #include <QGraphicsScene> #include <cmath> +#include <QGraphicsObject> +#include <QGraphicsItem> + #include "../../../Lib/Audio.hh" namespace Vivy @@ -17,16 +20,17 @@ class TimingAxis final : public QGraphicsObject { Q_OBJECT public: - explicit TimingAxis(AudioContext::StreamPtr stream, int x0, int x1, int y) noexcept; + explicit TimingAxis(AudioContext::StreamPtr stream) noexcept; ~TimingAxis() noexcept override = 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); + static inline constexpr QColor axisColour = QColor(255, 220, 220); + QPen pen; + int penWidth{ 1 }; - qreal penWidth{ 1 }; AudioContext::StreamPtr audioStream; /* @@ -34,23 +38,18 @@ private: * qPainter->drawLine() and qPainter->drawText() * restrict us to ints anyways */ - QVector<quint64> availableTicks = { 10, 100, 1000, 10000, 60000, 3600000, 86400000 }; - quint64 minBetweenMinor{ 10 }; - quint64 minorTicks; - quint64 majorTicks; - - int minorTicksHeight { 3 }; - int majorTicksHeight { 7 }; - int timeDigitsHeight { 20 }; - int timeDigitsMargin { 3 }; - int ticksBase { 30 }; - - int x0{ 0 }; - int x1{ 0 }; - int y{ 0 }; - - QString msToString(quint64 i) const noexcept; - inline quint64 posFromMs(quint64 t, quint64 width, quint64 length) const noexcept; + QVector<int> availableTicks = { 10, 100, 1000, 10000, 60000, 3600000, 86400000 }; + int minBetweenMinor{ 10 }; + int minorTicks; + int majorTicks; + + int minorTicksHeight{ 3 }; + int majorTicksHeight{ 7 }; + int timeDigitsHeight{ 20 }; + int timeDigitsMargin{ 3 }; + int height{ 30 }; + + QString msToString(int i) const noexcept; protected: public slots: diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc b/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc index b6e76efc68e086ae0326c675eba6e6d3df190084..b4327dedefe8aff068ca5f4593246997bc533e10 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc @@ -1,9 +1,5 @@ #include "TimingScene.hh" -#include "../../../Lib/Utils.hh" -#include "../../../Lib/Ass/Ass.hh" -#include "../../../Lib/Document/VivyDocument.hh" - #include <QGraphicsLineItem> #include <QGraphicsPixmapItem> #include <QGraphicsView> @@ -16,6 +12,12 @@ #include <QScrollBar> #include <QVBoxLayout> +#include "../../../Lib/Utils.hh" +#include "../../../Lib/Ass/Ass.hh" +#include "../../../Lib/Document/VivyDocument.hh" + +#include "TimingUtils.hh" + using namespace Vivy; TimingScene::TimingScene(QWidget *parent) noexcept @@ -28,36 +30,19 @@ TimingScene::TimingScene(QImage img_, AudioContext::StreamPtr stream, QWidget *p , img(img_) , audioStream(stream) { + ax = new TimingAxis(stream); + QPixmap pixmap(QPixmap::fromImage(img)); - setSceneRect(QRect(pixmap.rect())); backgroundImg = addPixmap(pixmap); - ax = new TimingAxis(stream, 0, pixmap.width(), 10); + setSceneRect(QRect(pixmap.rect())); addItem(ax); ax->refreshTicks(); - if (auto assDocument = currentVivyDocument->getAssSubDocument()){ + if (auto assDocument = currentVivyDocument->getAssSubDocument()) { QVector<Ass::LinePtr> lines = assDocument->getLines(); for (int i = 0; i < lines.size(); ++i) { - if (auto line = lines.at(i).get()){ - qreal xstart = posFromTime(line->getStart()); - TimingBar* bar = new TimingBar(QLine(int(xstart), 30, int(xstart), int(height())), QColor(127,0,0)); - bar->setZValue(10); - addItem(bar); - qreal xend = posFromTime(line->getEnd()); - bar = new TimingBar(QLine(int(xend), 30, int(xend), int(height())), QColor(127,0,0)); - bar->setZValue(10); - addItem(bar); - QGraphicsRectItem* rect = new QGraphicsRectItem(xstart, 30, xend-xstart, int(height())-30); - addItem(rect); - rect->setBrush(Qt::blue); - rect->setOpacity(0.1); - QGraphicsTextItem* text = new QGraphicsTextItem(line->getContentAsText()); - addItem(text); - text->setZValue(5); - text->setTextWidth(xend-xstart-10); - text->setPos(xstart+5, 30+(height()-30)/2); - } + if (auto line = lines.at(i).get()) {} } } } @@ -71,16 +56,10 @@ TimingScene::mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept if (p && (got == nullptr || got == backgroundImg)) { // Handle the different cases - switch(timingMode){ - case TimingMode::Line: - handleMousePressEventLine(event, p); - break; - case TimingMode::Syl: - handleMousePressEventSyl(event, p); - break; - case TimingMode::Char: - handleMousePressEventChar(event, p); - break; + switch (timingMode) { + case TimingMode::Line: handleMousePressEventLine(event, p); break; + case TimingMode::Syl: handleMousePressEventSyl(event, p); break; + case TimingMode::Char: handleMousePressEventChar(event, p); break; } } @@ -90,13 +69,13 @@ TimingScene::mousePressEvent(QGraphicsSceneMouseEvent *event) noexcept void TimingScene::handleMousePressEventLine(QGraphicsSceneMouseEvent *event, Ass::LinePtr p) noexcept { - QPointF pos = event->scenePos(); - quint64 time = timeFromPos(pos.x()); + QPointF pos = event->scenePos(); + int time = timeFromPos(int(pos.x())); if (const auto &btn = event->button(); btn == Qt::LeftButton) { - p->setStart(time); + p->setStart(quint64(time)); } else if (btn == Qt::RightButton) { - p->setEnd(time); + p->setEnd(quint64(time)); } } @@ -110,32 +89,16 @@ TimingScene::handleMousePressEventChar(QGraphicsSceneMouseEvent *, Ass::LinePtr) { } -quint64 -TimingScene::timeFromPos(qreal x) const noexcept -{ - return timeFromPos(quint64(x)); -} - -quint64 -TimingScene::timeFromPos(quint64 x) const noexcept +int +TimingScene::timeFromPos(int x) const noexcept { - if (const quint64 w = quint64(width()); x <= 0 || w <= 0) { - qCritical() << "Try avoid possible divide by zero in the time from position"; - return 0; - } else { - return x * audioStream->getLength() / w; - } + return TimingUtils::msFromPos(x, int(width()), int(audioStream->getLength())); } -qreal -TimingScene::posFromTime(quint64 t) const noexcept +int +TimingScene::posFromTime(int t) const noexcept { - if (const quint64 w = quint64(width()); t <= 0 || w <= 0) { - qCritical() << "Try avoid possible divide by zero in the position from time"; - return 0; - } else { - return 10 * t * w / audioStream->getLength(); - } + return TimingUtils::posFromMs(t, int(width()), int(audioStream->getLength())); } QGraphicsPixmapItem * diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh b/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh index c6cf5a2a6a372cdcde1d30a9ed33e5820e689d61..306a76558dc0e95791ed30d5aad07643de84e780 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh @@ -37,13 +37,14 @@ public: TimingAxis *getAxis(); private: - quint64 timeFromPos(qreal x) const noexcept; - quint64 timeFromPos(quint64 x) const noexcept; - qreal posFromTime(quint64 t) const noexcept; void handleMousePressEventLine(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; void handleMousePressEventSyl(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; void handleMousePressEventChar(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; +private: + inline int timeFromPos(int x) const noexcept; + inline int posFromTime(int t) const noexcept; + public slots: }; diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc new file mode 100644 index 0000000000000000000000000000000000000000..d9a398393d60b6b20c107df67cde980e7b5f1500 --- /dev/null +++ b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc @@ -0,0 +1,3 @@ +#include "TimingUtils.hh" + +using namespace Vivy; diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh new file mode 100644 index 0000000000000000000000000000000000000000..d286004d49db7e983c59b4b8c8c41e5f550bdb2f --- /dev/null +++ b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh @@ -0,0 +1,21 @@ +#pragma once + +#ifndef __cplusplus +#error "This is a C++ header" +#endif + +namespace Vivy +{ +class TimingUtils { +public: + static inline int posFromMs(int t, int audioWidth, int audioLength) noexcept + { + return audioLength == 0 ? 0 : int(qint64(t) * qint64(audioWidth) / audioLength); + } + + static inline int msFromPos(int x, int audioLength, int audioWidth) noexcept + { + return audioWidth == 0 ? 0 : int(qint64(x) * qint64(audioLength) / audioWidth); + } +}; +} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingView.cc b/src/UI/DocumentViews/AudioVisualizer/TimingView.cc index 531b08ea0cd9268d790be7acd7900412ff5f3e86..bf971817726a67389304aa8d59150f1fda2154f9 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingView.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingView.cc @@ -2,23 +2,22 @@ using namespace Vivy; -TimingView::TimingView(QImage img, AudioContext::StreamPtr stream, QWidget *parent) noexcept - : QGraphicsView(parent) +TimingView::TimingView(QGraphicsScene *scene, QImage img, AudioContext::StreamPtr stream, + QWidget *parent) noexcept + : QGraphicsView(scene, parent) , audioStream(stream) { - currentScene = new TimingScene(img, stream, this); - setFixedHeight(img.height()); - setMaximumHeight(img.height()); + //setFixedHeight(img.height()); + //setMaximumHeight(img.height()); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); QObject::connect(verticalScrollBar(), &QScrollBar::rangeChanged, this, &TimingView::moveScrollBarToBottom); verticalScrollBar()->setValue(verticalScrollBar()->maximum()); - horizontalScrollBar()->setValue(horizontalScrollBar()->minimum()); + //horizontalScrollBar()->setValue(horizontalScrollBar()->minimum()); - setScene(currentScene); - centerOn(0,height()); + centerOn(0, height()); } void diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingView.hh b/src/UI/DocumentViews/AudioVisualizer/TimingView.hh index d0fb66dc03b294959e759bed15bca47f62287cd4..ee8a3a60aa7a7454db1b5ac1111f67648c5ddb6d 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingView.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingView.hh @@ -7,8 +7,8 @@ #include "../../../Lib/Utils.hh" #include "../../../Lib/Audio.hh" #include "TimingBar.hh" -#include "TimingScene.hh" #include "TimingAxis.hh" +#include "TimingScene.hh" namespace Vivy { @@ -21,10 +21,11 @@ public: static inline constexpr QColor startColour = QColor(127, 0, 127); static inline constexpr QColor endColour = QColor(0, 127, 0); - explicit TimingView(QImage, AudioContext::StreamPtr, QWidget * = nullptr) noexcept; + explicit TimingView(QGraphicsScene *, QImage, AudioContext::StreamPtr, + QWidget * = nullptr) noexcept; ~TimingView() noexcept override = default; - TimingScene* getTimingScene() const; + TimingScene *getTimingScene() const; void wheelEvent(QWheelEvent *) noexcept override; private: diff --git a/src/UI/DocumentViews/MpvControls.cc b/src/UI/DocumentViews/MpvControls.cc index a9d63a8f2e9c0a55cc4c555c864cd1573a2d6128..9bcb033b09803cd3c6191754cd175514c2e9371a 100644 --- a/src/UI/DocumentViews/MpvControls.cc +++ b/src/UI/DocumentViews/MpvControls.cc @@ -10,7 +10,7 @@ MpvControls::MpvControls(MpvContainer *passedContainer, QWidget *parent) noexcep auto *progressBar = new QSlider(this); auto *togglePlaybackButton = new QPushButton(playIcon, "", this); // Be default MPV is paused auto *reCreateMpvButton = new QPushButton( - reCreateMpvIcon, "", this); // Recreate the MPV context if something went wrong + reCreateMpvIcon, "", this); // Recreate the MPV context if something went wrong progressBar->setTracking(false); progressBar->setOrientation(Qt::Horizontal);