diff --git a/TOFIX.md b/TOFIX.md new file mode 100644 index 0000000000000000000000000000000000000000..91e3e79ca49cab030c76f3456dd20201d23737d1 --- /dev/null +++ b/TOFIX.md @@ -0,0 +1,3 @@ +[ ] Save file: do not error on save = name +[ ] Load Ass: do not fail on Default style not defined +[ ] Save file: add .vivy extension by default diff --git a/src/UI/DocumentViews/AudioVisualizer.cc b/src/UI/DocumentViews/AudioVisualizer.cc index 311045a665be4117da22d5e4bf91eac7921d8f4c..cad48f1051ebfd8110784eea32cb581376471880 100644 --- a/src/UI/DocumentViews/AudioVisualizer.cc +++ b/src/UI/DocumentViews/AudioVisualizer.cc @@ -71,19 +71,19 @@ AudioVisualizer::printSpectrum(QImage pixmap, AudioContext::StreamPtr stream) no { TimingScene *timingScene = new TimingScene(pixmap, stream, rootVivyDocumentView, 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 timing scene in itself QGridLayout *layout = new QGridLayout; layout->addWidget(timingView, 1, 0); layout->setColumnStretch(0, 10); - layout->addWidget(params, 1, 1); + layout->addWidget(timingScene->getParams(), 1, 1); layout->setColumnStretch(1, 0); setLayout(layout); - connect(params->getZoomSlider(), &QSlider::valueChanged, timingScene->getAxis(), - &TimingAxis::refreshTicks); - connect(params->getRebuildSceneButton(), &QPushButton::released, timingScene, + connect(timingScene->getParams()->getZoomSlider(), &QSlider::valueChanged, timingScene->getParams(), &TimingParams::setAudioWidthScale); + // FIXME: remove for now, may freeze PC + //connect(timingScene->getParams(), &TimingParams::paramsChanged, timingScene, &TimingScene::rebuildScene); + connect(timingScene->getParams()->getRebuildSceneButton(), &QPushButton::released, timingScene, &TimingScene::rebuildScene); connect(&rootVivyDocumentView, &VivyDocumentView::assSubDocumentChanged, timingScene, &TimingScene::rebuildScene); diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc index 986b1c6da958d23b3a72f499105af9a18f5fdabd..5dd4d5fd58e5d4b56161fc24270a3389f29ba4f4 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.cc @@ -1,6 +1,6 @@ #include "TimingAxis.hh" -#include "TimingUtils.hh" +#include "TimingParams.hh" using namespace Vivy; @@ -9,27 +9,22 @@ TimingAxis::TimingAxis() noexcept { pen = QPen(axisColour); pen.setWidth(penWidth); - refreshTicks(); } QRectF TimingAxis::boundingRect() const { - return QRectF(0, penWidth, TimingUtils::audioWidth() + 2 * penWidth, - -TimingUtils::axisHeight() - penWidth); + return QRectF(0, 10+penWidth, getTimingParam()->audioWidth() + 2 * penWidth, + -2*getTimingParam()->axisHeight() - penWidth); } -/* - * Refresh the minor/major ticks value - * For intended results, this function should only be called when scene() returns a valid QGraphicsScene (so not in the constructors) - */ void -TimingAxis::refreshTicks() +TimingAxis::onParamsChanged() { int nbAvailableTicks = availableTicks.size(); int minorTicksIndex = 0; - int length = TimingUtils::audioLength(); - int width = TimingUtils::audioWidth(); + int length = getTimingParam()->audioLength(); + int width = getTimingParam()->audioWidth(); if (width != 0 && length != 0) { for (minorTicksIndex = 0; minorTicksIndex < nbAvailableTicks; minorTicksIndex++) { if (width * availableTicks[minorTicksIndex] / length >= minBetweenMinor) { @@ -46,13 +41,13 @@ TimingAxis::refreshTicks() minorTicks = 0; majorTicks = availableTicks[minorTicksIndex]; } - TimingUtils::setTicks(minorTicks, majorTicks); + getTimingParam()->setTicks(minorTicks, majorTicks); } void TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - int audioLength = TimingUtils::audioLength(); + int audioLength = getTimingParam()->audioLength(); if (audioLength == 0) return; @@ -70,13 +65,13 @@ TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWi * should look whether this precision loss is negligible or not */ for (int i = 0; i < audioLength; i += majorTicks) { - int pos = TimingUtils::posFromMs(i); - painter->drawText(QPoint(pos, yText), TimingUtils::printMajorTicks(i)); + int pos = getTimingParam()->posFromMs(i); + painter->drawText(QPoint(pos, yText), getTimingParam()->printMajorTicks(i)); painter->drawLine(pos, 0, pos, -majorTicksHeight); } if (minorTicks > 0) { for (int i = 0; i < audioLength; i += minorTicks) { - int pos = TimingUtils::posFromMs(i); + int pos = getTimingParam()->posFromMs(i); if (Q_LIKELY(fmod(i, majorTicks) != 0)) painter->drawLine(pos, 0, pos, -minorTicksHeight); } diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh index d2cfd923ff9f9dbbaf409c34e5008aee5a4683fe..266c0431373e7e2df91b388cfe1e3d5998d642c0 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingAxis.hh @@ -42,7 +42,7 @@ private: protected: public slots: - void refreshTicks(); + void onParamsChanged(); }; } diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc index 6896039c9b5c437c705939b89141507030fec719..9927333e5b94c9b1dc828fa8b9c14981688d481d 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.cc @@ -1,6 +1,6 @@ #include "TimingCursor.hh" -#include "TimingUtils.hh" +#include "TimingParams.hh" using namespace Vivy; @@ -8,25 +8,23 @@ TimingCursor::TimingCursor() : QGraphicsItem() { setZValue(Z_CURSOR_BAR); - textRect = QRectF(0, 10, maxWidth, - TimingUtils::audioHeight() - 20); // TODO : remove 10/20 magic numbers } QRectF TimingCursor::boundingRect() const { - return QRectF(-maxWidth, 0, maxWidth * 2, TimingUtils::audioHeight()); + return QRectF(-maxWidth, 0, maxWidth * 2, getTimingParam()->audioHeight()); } void TimingCursor::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->drawLine(0, 0, 0, TimingUtils::audioHeight()); + painter->drawLine(0, 0, 0, getTimingParam()->audioHeight()); QRectF textRectangle = textRect; QPointF sceneLeft = mapFromScene(QPointF(0, 0)); QPointF sceneRight = mapFromScene(QPointF(scene()->sceneRect().right(), 0)); - TimingUtils::adjustFlip(&textRectangle, sceneLeft.rx(), sceneRight.rx()); + getTimingParam()->adjustFlip(&textRectangle, sceneLeft.rx(), sceneRight.rx()); //painter->drawRect(textRectangle); painter->drawText(textRectangle, Qt::AlignHCenter | Qt::AlignTop, cursorTime); @@ -37,3 +35,10 @@ TimingCursor::setTime(QString str) noexcept { cursorTime = str; } + +void +TimingCursor::onParamsChanged() +{ + textRect = QRectF(0, 10, maxWidth, + getTimingParam()->audioHeight() - 20); // TODO : remove 10/20 magic numbers +} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh index c7c72ff32d6a5b13eb609800090b5aa5abd8dafd..130f2b99db958559bed0e08639bf7ff06eed7a28 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingCursor.hh @@ -24,6 +24,9 @@ private: public: void setTime(QString str) noexcept; + +public slots: + void onParamsChanged(); }; } diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingLine.cc b/src/UI/DocumentViews/AudioVisualizer/TimingLine.cc index a51875c86c38b3a352aeac2e346a1c12bd41be7e..c64a40e1c0b039970ea6d535e525e954fe004aa3 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingLine.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingLine.cc @@ -1,69 +1,30 @@ #include "TimingLine.hh" -#include "TimingUtils.hh" +#include "TimingParams.hh" using namespace Vivy; -#define CONNECT_SEP(sep) \ - connect(sep, &TimingSeparator::positionChanged, this, &TimingLine::timingSeparatorHasChanged); \ - connect(sep, &TimingSeparator::enterPress, this, &TimingLine::sepEnterPress); \ - connect(sep, &TimingSeparator::exitPress, this, &TimingLine::sepExitPress); - TimingLine::TimingLine(Ass::Line& _line, int index, QGraphicsItem *parent) : QGraphicsObject(parent) , line(_line) , lineIndex(index) { - setPos(TimingUtils::posFromMs(int(line.getStart()) * 10), TimingUtils::axisHeight()); - int currentTime = 0; - int endSyl = 0; - int i; - - setZValue(Z_LINE_BACKGROUND); - - TimingSeparator *timingSeparatorStart = - new TimingSeparator(currentTime, 0, TimingSeparator::SeparatorStyle::Start, this); - seps.append(timingSeparatorStart); - CONNECT_SEP(timingSeparatorStart); - - QVector<Ass::Syl> syls = line.getContent(); - for (i = 0; i < syls.size(); ++i) { - endSyl = currentTime + 10 * int(syls.at(i).getDuration()); - - TimingSyl *timingSyl = new TimingSyl(syls.at(i), currentTime, this); - timingSyls.append(timingSyl); - - if (i != 0) { - TimingSeparator *timingSeparator = - new TimingSeparator(currentTime, i, TimingSeparator::SeparatorStyle::Middle, this); - seps.append(timingSeparator); - CONNECT_SEP(timingSeparator); - } - - currentTime = endSyl; - } - - TimingSeparator *timingSeparatorEnd = - new TimingSeparator(currentTime, i, TimingSeparator::SeparatorStyle::End, this); - seps.append(timingSeparatorEnd); - CONNECT_SEP(timingSeparatorEnd); } -#undef CONNECT_SEP QRectF TimingLine::boundingRect() const { return QRectF(tempOffset, 0, - TimingUtils::posFromMs(int(10 * (line.getEnd() - line.getStart()))), - TimingUtils::audioHeight()); + getTimingParam()->posFromMs(int(10 * (line.getEnd() - line.getStart()))), + getTimingParam()->audioHeight()); } void TimingLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->fillRect(QRectF(tempOffset, 0, - TimingUtils::posFromMs(int(10 * (line.getEnd() - line.getStart()))), - TimingUtils::audioHeight()), + getTimingParam()->posFromMs(int(10 * (line.getEnd() - line.getStart()))), + getTimingParam()->audioHeight()), QColor(0, 255, 255, 50)); } @@ -95,7 +56,7 @@ TimingLine::sepExitPress(int sepIndex) seps[i]->silentlyMoveBy(-tempOffset, 0); } - line.setStart(quint64(TimingUtils::msFromPos(mapToScene(0, 0).x()) / 10)); + line.setStart(quint64(getTimingParam()->msFromPos(mapToScene(0, 0).x()) / 10)); } } @@ -116,16 +77,16 @@ TimingLine::requestMove(int sepIndex, qreal x) mini = sceneRect.left(); maxi = sepIndex < syls.size() - 1 ? timingSyls[sepIndex + 1]->pos().x() - : TimingUtils::posFromMs(int(line.getDuration()) * 10); + : getTimingParam()->posFromMs(int(line.getDuration()) * 10); given = qBound(mini, given, maxi); tempOffset = given; - quint64 dur1 = quint64(TimingUtils::msFromPos(int(seps[1]->pos().x() - given)) / 10); + quint64 dur1 = quint64(getTimingParam()->msFromPos(int(seps[1]->pos().x() - given)) / 10); syls[0].setDuration(dur1); timingSyls[0]->setLen(dur1); timingSyls[0]->setPos(given, 0); - line.setStart(quint64(TimingUtils::msFromPos(mapToScene(seps[0]->pos().x(), 0).x()) / 10)); + line.setStart(quint64(getTimingParam()->msFromPos(mapToScene(seps[0]->pos().x(), 0).x()) / 10)); } else if (sepIndex >= syls.size()) { @@ -136,21 +97,21 @@ TimingLine::requestMove(int sepIndex, qreal x) given = qBound(mini, given, maxi); quint64 dur2 = - quint64(TimingUtils::msFromPos(int(given - timingSyls[sepIndex - 1]->pos().x())) / 10); + quint64(getTimingParam()->msFromPos(int(given - timingSyls[sepIndex - 1]->pos().x())) / 10); syls[sepIndex - 1].setDuration(dur2); timingSyls[sepIndex - 1]->setLen(dur2); - line.setEnd(line.getStart() + quint64(TimingUtils::msFromPos(int(given)) / 10)); + line.setEnd(line.getStart() + quint64(getTimingParam()->msFromPos(int(given)) / 10)); } else { mini = timingSyls[sepIndex - 1]->pos().x(); maxi = sepIndex < syls.size() - 1 ? timingSyls[sepIndex + 1]->pos().x() - : TimingUtils::posFromMs(int(line.getDuration() * 10)); + : getTimingParam()->posFromMs(int(line.getDuration() * 10)); given = qBound(mini, given, maxi); quint64 sumDur = syls[sepIndex].getDuration() + syls[sepIndex - 1].getDuration(); quint64 dur1 = quint64( - TimingUtils::msFromPos(int(given) - int(timingSyls[sepIndex - 1]->pos().x())) / 10); + getTimingParam()->msFromPos(int(given) - int(timingSyls[sepIndex - 1]->pos().x())) / 10); dur1 = qMin(dur1, sumDur); quint64 dur2 = sumDur - dur1; @@ -168,3 +129,46 @@ TimingLine::requestMove(int sepIndex, qreal x) return given; } + +#define CONNECT_SEP(sep) \ + connect(sep, &TimingSeparator::positionChanged, this, &TimingLine::timingSeparatorHasChanged); \ + connect(sep, &TimingSeparator::enterPress, this, &TimingLine::sepEnterPress); \ + connect(sep, &TimingSeparator::exitPress, this, &TimingLine::sepExitPress); +void +TimingLine::onParamsChanged() +{ + setPos(getTimingParam()->posFromMs(int(line.getStart()) * 10), getTimingParam()->axisHeight()); + int currentTime = 0; + int endSyl = 0; + int i; + + setZValue(Z_LINE_BACKGROUND); + + TimingSeparator *timingSeparatorStart = + new TimingSeparator(currentTime, 0, TimingSeparator::SeparatorStyle::Start, this); + seps.append(timingSeparatorStart); + CONNECT_SEP(timingSeparatorStart); + + QVector<Ass::Syl> syls = line.getContent(); + for (i = 0; i < syls.size(); ++i) { + endSyl = currentTime + 10 * int(syls.at(i).getDuration()); + + TimingSyl *timingSyl = new TimingSyl(syls.at(i), currentTime, this); + timingSyls.append(timingSyl); + + if (i != 0) { + TimingSeparator *timingSeparator = + new TimingSeparator(currentTime, i, TimingSeparator::SeparatorStyle::Middle, this); + seps.append(timingSeparator); + CONNECT_SEP(timingSeparator); + } + + currentTime = endSyl; + } + + TimingSeparator *timingSeparatorEnd = + new TimingSeparator(currentTime, i, TimingSeparator::SeparatorStyle::End, this); + seps.append(timingSeparatorEnd); + CONNECT_SEP(timingSeparatorEnd); +} +#undef CONNECT_SEP diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingLine.hh b/src/UI/DocumentViews/AudioVisualizer/TimingLine.hh index 261d37a71821b945cbdbab6e7c71bb5eebd0cbfb..a92492815aafb944690b979cd640e262bb2b529f 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingLine.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingLine.hh @@ -34,6 +34,7 @@ public slots: void timingSeparatorHasChanged(int, qreal); void sepEnterPress(int); void sepExitPress(int); + void onParamsChanged(); }; } diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingParams.cc b/src/UI/DocumentViews/AudioVisualizer/TimingParams.cc index 83baf547a6ee6dab30d83d27835d7496a1a09ba8..b18c1c00812f4e4535ef984441d6702ca8a0246d 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingParams.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingParams.cc @@ -11,11 +11,13 @@ TimingParams::TimingParams(QWidget *parent) noexcept zoomSlider = std::make_unique<QSlider>(new QSlider(Qt::Vertical)); if (QSlider *slider = zoomSlider.get()) { - slider->setMinimum(0); + slider->setMinimum(1); slider->setMaximum(100); slider->setSingleStep(0); slider->setPageStep(0); slider->setValue(0); + // FIXME: set to false for now as it may be intensive while rebuilding the scene isn't optimized + slider->setTracking(false); hlayout->addWidget(slider); } sensibilitySlider = std::make_unique<QSlider>(new QSlider(Qt::Vertical)); @@ -25,6 +27,8 @@ TimingParams::TimingParams(QWidget *parent) noexcept slider->setSingleStep(0); slider->setPageStep(0); slider->setValue(0); + // FIXME: set to false for now as it may be intensive while rebuilding the scene isn't optimized + slider->setTracking(false); hlayout->addWidget(slider); } @@ -53,3 +57,64 @@ TimingParams::getRebuildSceneButton() const noexcept { return rebuildSceneButton.get(); } + +void +TimingParams::adjustFlip(QRectF *rect, qreal left, qreal right) noexcept +{ + if (Q_UNLIKELY(right - rect->right() < 0)) { + qreal w = rect->width(); + rect->adjust(-w, 0, -w, 0); + } +} + +void +TimingParams::adjustTranslate(QRectF *rect, qreal left, qreal right) noexcept +{ + qreal d; + qreal hw = rect->width() / 2; + if (Q_UNLIKELY((d = hw + left) > 0)) { + rect->adjust(d, 0, d, 0); + } + if (Q_UNLIKELY((d = right - hw) < 0)) { + rect->adjust(d, 0, d, 0); + } +} + +QString +TimingParams::printMajorTicks(int t) noexcept +{ + // TODO : adapt to different ticks (not everytime with "." first) + QString ret( + QStringLiteral("%1").arg(t % 60000 / 1000, t >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + + QString(".") + QString::number(t % 1000 / m_majorTicks)); + if (t >= 60000) + ret.prepend(QString::number(t / 60000) + QString(":")); + if (t >= 360000) + ret.prepend(QString::number(t / 360000) + QString(":")); + return ret; +} + +QString +TimingParams::printCursor(int t) noexcept +{ + int absT = abs(t); + // TODO : adapt to different ticks (not everytime with "." first) + QString ret( + QString::fromLatin1(t < 0 ? "-" : "" ) + + QStringLiteral("%1").arg(absT % 60000 / 1000, absT >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + + QString(".") + QString::number(absT % 1000 / m_minorTicks)); + if (absT >= 60000) + ret.prepend(QString::number(absT / 60000) + QString(":")); + if (absT >= 360000) + ret.prepend(QString::number(absT / 360000) + QString(":")); + return ret; +} + +void +TimingParams::updateRatios() noexcept +{ + m_wl = m_audioLength != 0 ? qreal(m_audioWidth) / qreal(m_audioLength) : 0; + m_lw = m_audioWidth != 0 ? qreal(m_audioLength) / qreal(m_audioWidth) : 0; + + emit paramsChanged(); +} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingParams.hh b/src/UI/DocumentViews/AudioVisualizer/TimingParams.hh index 048caa6b3e8361692ed1f091688d2d83aa800676..b2103f304b68355292ae9e9d218324d10792fc40 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingParams.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingParams.hh @@ -7,6 +7,18 @@ #include "PreCompiledHeaders.hh" #include "Lib/Utils.hh" +#include "TimingScene.hh" + +#define getTimingScene() static_cast<TimingScene *>(scene()) +#define getTimingParam() getTimingScene()->getParams() + +#define Z_SPECTER -1000 +#define Z_AXIS 10 +#define Z_LINE_BACKGROUND -100 +#define Z_SEPARATOR_START_END 1000 +#define Z_SEPARATOR_MIDDLE 100 +#define Z_LINE_SYL_TEXT 50 +#define Z_CURSOR_BAR 500 namespace Vivy { @@ -25,6 +37,72 @@ public: QSlider *getZoomSlider() const noexcept; QSlider *getSensibilitySlider() const noexcept; QPushButton *getRebuildSceneButton() const noexcept; + +private: + // Managed by TimingAxis + int m_majorTicks{1}, m_minorTicks{1}; + // Managed by TimingScene + int m_axisHeight{1}, m_audioWidth{1}, m_audioLength{1}, m_audioHeight{1}; + // Self-managed + qreal m_wl{0}, m_lw{0}; + qreal m_audioWidthScale{1}; + +public: + void adjustTranslate(QRectF *, qreal, qreal) noexcept; + void adjustFlip(QRectF *, qreal, qreal) noexcept; + + QString printMajorTicks(int) noexcept; + QString printCursor(int) noexcept; + + inline void setTicks(int t, int T) noexcept + { + m_minorTicks = t; + m_majorTicks = T; + }; + inline void setAxisHeight(int x) { m_axisHeight = x; }; + inline void setAudioHeight(int x) { m_audioHeight = x; }; + inline void setAudioWidth(int x) + { + m_audioWidth = int(x * m_audioWidthScale); + updateRatios(); + }; + inline void setAudioLength(int x) + { + m_audioLength = x; + updateRatios(); + }; + + inline int axisHeight() { return m_axisHeight; }; + inline int audioHeight() { return m_audioHeight; }; + inline int audioWidth() { return m_audioWidth; }; + inline int audioLength() { return m_audioLength; }; + + inline void setAudioWidthScale(int s) + { + m_audioWidthScale = 1 + 0.01 * s; + setAudioWidth(m_audioWidth); + updateRatios(); + }; + + template <typename T> T posFromMs(T t) noexcept { + return T(t * m_wl); + } + template <typename T> T posFromMs(T t, int audioWidth, int audioLength) noexcept { + return audioLength == 0 ? 0 : T(qint64(t) * qint64(audioWidth) / audioLength); + } + + template <typename T> T msFromPos(T x) noexcept { + return T(x * m_lw); + } + template <typename T> T msFromPos(T x, int audioLength, int audioWidth) noexcept { + return audioWidth == 0 ? 0 : int(qint64(x) * qint64(audioLength) / audioWidth); + } + +private: + void updateRatios() noexcept; + +signals: + void paramsChanged(); }; } diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc b/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc index 39760c37eeb61773c695e8ec30c09c574b1c70f6..4fae53b09b44c2a81512a4e3ff64aa64c3480c54 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingScene.cc @@ -1,6 +1,6 @@ #include "TimingScene.hh" -#include "TimingUtils.hh" +#include "TimingParams.hh" using namespace Vivy; @@ -16,6 +16,10 @@ TimingScene::TimingScene(QImage img_, AudioContext::StreamPtr stream, VivyDocume , img(img_) , audioStream(stream) { + params = new TimingParams(); + // We put a common origin for the items at the bottom of the axis + params->setAxisHeight(30); // TODO: remove magic number + rebuildScene(); } @@ -36,37 +40,38 @@ TimingScene::rebuildScene() } timingLines.clear(); - // We put a common origin for the items at the bottom of the axis - TimingUtils::setAxisHeight(30); // TODO: remove magic number - QPixmap pixmap(QPixmap::fromImage(img)); backgroundImg = addPixmap(pixmap); backgroundImg->setZValue(Z_SPECTER); - backgroundImg->setPos(0, TimingUtils::axisHeight()); + backgroundImg->setPos(0, params->axisHeight()); - TimingUtils::setAudioHeight(pixmap.height()); - TimingUtils::setAudioWidth(img.width()); - TimingUtils::setAudioLength(int(audioStream->getLength())); + params->setAudioHeight(pixmap.height()); + params->setAudioWidth(img.width()); + params->setAudioLength(int(audioStream->getLength())); ax = new TimingAxis(); addItem(ax); + ax->onParamsChanged(); ax->setZValue(Z_AXIS); - ax->setPos(0, TimingUtils::axisHeight()); - - // Freeze the scene boundaries - setSceneRect(sceneRect()); + ax->setPos(0, params->axisHeight()); cursor = new TimingCursor(); addItem(cursor); - cursor->setPos(0, TimingUtils::axisHeight()); + cursor->onParamsChanged(); + cursor->setPos(0, params->axisHeight()); + + // Freeze the scene boundaries + QRectF sRect = sceneRect(); + setSceneRect(QRectF(0, sRect.top(), params->audioWidth(), sRect.height()).normalized()); if (auto assDocument = rootVivyDocument.getAssSubDocument()) { QVector<Ass::LinePtr> lines = assDocument->getLines(); for (int i = 0; i < lines.size(); ++i) { if (auto line = lines.at(i)) { TimingLine *l = new TimingLine(*line, i); - l->setVisible(false); addItem(l); + l->onParamsChanged(); + l->setVisible(false); timingLines.append(l); if (auto assLinesModel = rootVivyDocumentView.getAssLinesModel()) { connect(l, &TimingLine::lineChanged, assLinesModel, &AssLinesModel::updateLine); @@ -105,7 +110,7 @@ TimingScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) noexcept { int x = int(mouseEvent->scenePos().rx()); cursor->setPos(x, 30); - cursor->setTime(TimingUtils::printCursor(TimingUtils::msFromPos(x))); + cursor->setTime(params->printCursor(params->msFromPos(x))); QGraphicsScene::mouseMoveEvent(mouseEvent); } @@ -113,7 +118,7 @@ void TimingScene::handleMousePressEventLine(QGraphicsSceneMouseEvent *event, Ass::LinePtr p) noexcept { QPointF pos = event->scenePos(); - int time = TimingUtils::msFromPos(int(pos.x())); + int time = params->msFromPos(int(pos.x())); if (const auto &btn = event->button(); btn == Qt::LeftButton) { p->setStart(quint64(time)); @@ -144,6 +149,12 @@ TimingScene::getAxis() return ax; } +TimingParams * +TimingScene::getParams() +{ + return params; +} + void TimingScene::updateSelectedLines(const QItemSelection &selected, const QItemSelection &deselected) { diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh b/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh index 0120029bcb1d3165b41a5fe4743303c15a7f2a4a..7e275b9f37eb1a283ce8906043b8c591961c57f6 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh +++ b/src/UI/DocumentViews/AudioVisualizer/TimingScene.hh @@ -14,8 +14,11 @@ #include "TimingCursor.hh" #include "TimingLine.hh" + namespace Vivy { +class TimingParams; + class TimingScene final : public QGraphicsScene, public AbstractVivyDocumentNeeder { Q_OBJECT @@ -38,7 +41,10 @@ private: AudioContext::StreamPtr audioStream; Ass::LineWeakPtr currentLine{}; TimingMode timingMode{ TimingMode::Line }; - TimingAxis *ax{ nullptr }; + + // FIXME: add destructors + TimingParams *params; + TimingAxis *ax; QVector<TimingLine *> timingLines; @@ -48,6 +54,7 @@ public: void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) noexcept override; TimingAxis *getAxis(); + TimingParams *getParams(); private: void handleMousePressEventLine(QGraphicsSceneMouseEvent *, Ass::LinePtr) noexcept; diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingSeparator.cc b/src/UI/DocumentViews/AudioVisualizer/TimingSeparator.cc index 82e48367d58091e0e571fbf3199348f2f0f1c3cd..2c7d62c6a66f46f8bff3bad2bc0add9591cfa225 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingSeparator.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingSeparator.cc @@ -1,6 +1,6 @@ #include "TimingSeparator.hh" -#include "TimingUtils.hh" +#include "TimingParams.hh" #include "TimingLine.hh" using namespace Vivy; @@ -11,7 +11,7 @@ TimingSeparator::TimingSeparator(int time, int index, SeparatorStyle style_, Tim , sepIndex(index) , parentTimingLine(parent) { - setPos(TimingUtils::posFromMs(time), 0); + setPos(getTimingParam()->posFromMs(time), 0); setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); @@ -38,14 +38,14 @@ TimingSeparator::TimingSeparator(int time, int index, SeparatorStyle style_, Tim QRectF TimingSeparator::boundingRect() const { - return QRectF(-widthPaw, 0, 2 * widthPaw, TimingUtils::audioHeight()); + return QRectF(-widthPaw, 0, 2 * widthPaw, getTimingParam()->audioHeight()); } void TimingSeparator::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { painter->setPen(pen); - int height = TimingUtils::audioHeight(); + int height = getTimingParam()->audioHeight(); int top = 1 + pen.width() / 2; int bottom = height - 1 - pen.width() / 2; diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingSyl.cc b/src/UI/DocumentViews/AudioVisualizer/TimingSyl.cc index f38ece882829c62bcf2109db55028b354e012703..7202ed4e2647602af9f97a9b03385c3e83837575 100644 --- a/src/UI/DocumentViews/AudioVisualizer/TimingSyl.cc +++ b/src/UI/DocumentViews/AudioVisualizer/TimingSyl.cc @@ -1,6 +1,6 @@ #include "TimingSyl.hh" -#include "TimingUtils.hh" +#include "TimingParams.hh" #include "TimingSeparator.hh" using namespace Vivy; @@ -10,13 +10,13 @@ TimingSyl::TimingSyl(Ass::Syl syl_, int startTime, QGraphicsItem *parent) , syl(syl_) { setZValue(Z_LINE_SYL_TEXT); - setPos(TimingUtils::posFromMs(startTime), 0); + setPos(getTimingParam()->posFromMs(startTime), 0); } QRectF TimingSyl::boundingRect() const { - return QRectF(0, 0, int(syl.getDuration()), TimingUtils::audioHeight()); + return QRectF(0, 0, int(syl.getDuration()), getTimingParam()->audioHeight()); } void diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc deleted file mode 100644 index 7e710002c5ce47d9fd0826a2a41d1a9ce39da704..0000000000000000000000000000000000000000 --- a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.cc +++ /dev/null @@ -1,95 +0,0 @@ -#include "TimingUtils.hh" - -using namespace Vivy; - -int TimingUtils::m_majorTicks{ 1 }; -int TimingUtils::m_minorTicks{ 1 }; - -int TimingUtils::m_axisHeight{ 1 }; -int TimingUtils::m_audioHeight{ 1 }; -int TimingUtils::m_audioWidth{ 1 }; -int TimingUtils::m_audioLength{ 1 }; - -qreal TimingUtils::m_wl{ 0 }; -qreal TimingUtils::m_lw{ 0 }; - -int -TimingUtils::posFromMs(int t) noexcept -{ - return int(t * m_wl); -} - -int -TimingUtils::posFromMs(int t, int audioWidth, int audioLength) noexcept -{ - return audioLength == 0 ? 0 : int(qint64(t) * qint64(audioWidth) / audioLength); -} - -int -TimingUtils::msFromPos(int x) noexcept -{ - return int(x * m_lw); -} - -int -TimingUtils::msFromPos(int x, int audioLength, int audioWidth) noexcept -{ - return audioWidth == 0 ? 0 : int(qint64(x) * qint64(audioLength) / audioWidth); -} - -void -TimingUtils::adjustFlip(QRectF *rect, qreal left, qreal right) noexcept -{ - if (Q_UNLIKELY(right - rect->right() < 0)) { - qreal w = rect->width(); - rect->adjust(-w, 0, -w, 0); - } -} - -void -TimingUtils::adjustTranslate(QRectF *rect, qreal left, qreal right) noexcept -{ - qreal d; - qreal hw = rect->width() / 2; - if (Q_UNLIKELY((d = hw + left) > 0)) { - rect->adjust(d, 0, d, 0); - } - if (Q_UNLIKELY((d = right - hw) < 0)) { - rect->adjust(d, 0, d, 0); - } -} - -QString -TimingUtils::printMajorTicks(int t) noexcept -{ - // TODO : adapt to different ticks (not everytime with "." first) - QString ret( - QStringLiteral("%1").arg(t % 60000 / 1000, t >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + - QString(".") + QString::number(t % 1000 / m_majorTicks)); - if (t >= 60000) - ret.prepend(QString::number(t / 60000) + QString(":")); - if (t >= 360000) - ret.prepend(QString::number(t / 360000) + QString(":")); - return ret; -} - -QString -TimingUtils::printCursor(int t) noexcept -{ - // TODO : adapt to different ticks (not everytime with "." first) - QString ret( - QStringLiteral("%1").arg(t % 60000 / 1000, t >= 60000 ? 2 : 1, 10, QLatin1Char('0')) + - QString(".") + QString::number(t % 1000 / m_minorTicks)); - if (t >= 60000) - ret.prepend(QString::number(t / 60000) + QString(":")); - if (t >= 360000) - ret.prepend(QString::number(t / 360000) + QString(":")); - return ret; -} - -void -TimingUtils::updateRatios() noexcept -{ - m_wl = m_audioLength != 0 ? qreal(m_audioWidth) / qreal(m_audioLength) : 0; - m_lw = m_audioWidth != 0 ? qreal(m_audioLength) / qreal(m_audioWidth) : 0; -} diff --git a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh b/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh deleted file mode 100644 index 2937527b60d7130dc805b6402aab6b25bb14b9b3..0000000000000000000000000000000000000000 --- a/src/UI/DocumentViews/AudioVisualizer/TimingUtils.hh +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#ifndef __cplusplus -#error "This is a C++ header" -#endif - -#include <PreCompiledHeaders.hh> - -#define getTimingScene() static_cast<TimingScene *>(scene()) - -#define Z_SPECTER -1000 -#define Z_AXIS 10 -#define Z_LINE_BACKGROUND -100 -#define Z_SEPARATOR_START_END 1000 -#define Z_SEPARATOR_MIDDLE 100 -#define Z_LINE_SYL_TEXT 50 -#define Z_CURSOR_BAR 500 - -/* - * FIXME - * Relying on static may pose issues when multiple audioVisualiser - * are open (when multiple VivyDocuments are open) - * Solution : yeet the statics and put an instance of this class - * in the TimingScene - */ -namespace Vivy -{ -class TimingUtils { -private: - // Managed by TimingAxis - static int m_majorTicks, m_minorTicks; - // Managed by TimingScene - static int m_axisHeight, m_audioWidth, m_audioLength, m_audioHeight; - // Self-managed - static qreal m_wl, m_lw; - -public: - static int posFromMs(int t, int audioWidth, int audioLength) noexcept; - static int posFromMs(int t) noexcept; - - static int msFromPos(int x, int audioLength, int audioWidth) noexcept; - static int msFromPos(int x) noexcept; - - static void adjustTranslate(QRectF *, qreal, qreal) noexcept; - static void adjustFlip(QRectF *, qreal, qreal) noexcept; - - static QString printMajorTicks(int) noexcept; - static QString printCursor(int) noexcept; - - static inline void setTicks(int t, int T) noexcept - { - m_minorTicks = t; - m_majorTicks = T; - }; - static inline void setAxisHeight(int x) { m_axisHeight = x; }; - static inline void setAudioHeight(int x) { m_audioHeight = x; }; - static inline void setAudioWidth(int x) - { - m_audioWidth = x; - updateRatios(); - }; - static inline void setAudioLength(int x) - { - m_audioLength = x; - updateRatios(); - }; - - static inline int axisHeight() { return m_axisHeight; }; - static inline int audioHeight() { return m_audioHeight; }; - static inline int audioWidth() { return m_audioWidth; }; - static inline int audioLength() { return m_audioLength; }; - -private: - static void updateRatios() noexcept; -}; -}