diff --git a/src/Lib/Document/VivyDocument.cc b/src/Lib/Document/VivyDocument.cc index 6cff0405c0ea45aa91b05ce900c7660d150acbbc..f9406f5ee2b3deec5345545448af7bcea8a22a36 100644 --- a/src/Lib/Document/VivyDocument.cc +++ b/src/Lib/Document/VivyDocument.cc @@ -308,6 +308,14 @@ VivyDocument::getAssSubDocument() const noexcept return assDocument; } +AudioContext::StreamPtr +VivyDocument::getAudioStream() const noexcept +{ + if (auto p = audioDocument.get()) + return audioDocument->getDefaultStream(); + return AudioContext::StreamPtr(); +} + bool VivyDocument::checkDocumentCapabilities(Capabilities asType) const noexcept { diff --git a/src/Lib/Document/VivyDocument.hh b/src/Lib/Document/VivyDocument.hh index b1476d7ca29f272adcd8807a804da9f9e6e00501..f52a3d9cb3790d44c79b1dcd4456beb3cdc7f790 100644 --- a/src/Lib/Document/VivyDocument.hh +++ b/src/Lib/Document/VivyDocument.hh @@ -7,7 +7,8 @@ #include "../Utils.hh" #include "../AbstractDocument.hh" -#include "../Uuid.hh" +#include "../AbstractDocument.hh" +#include "../Audio.hh" #include "CRTPSubDocument.hh" #define DCL_VIVY_SAVE_KEY(name) static const inline QString Key##name = QStringLiteral(#name); @@ -87,6 +88,8 @@ public: std::shared_ptr<VideoSubDocument> getVideoSubDocument() const noexcept; std::shared_ptr<AssSubDocument> getAssSubDocument() const noexcept; + AudioContext::StreamPtr getAudioStream() const noexcept; + QString getDocumentCapabilitiesString() const noexcept; bool checkDocumentCapabilities(Capabilities) const noexcept; diff --git a/src/UI/DocumentViews/AudioVisualizer.cc b/src/UI/DocumentViews/AudioVisualizer.cc index 8c641453f999dd9407276bee9206b3229ccbfa8b..f48c885e230a0d43675bf75c0e05066c24722018 100644 --- a/src/UI/DocumentViews/AudioVisualizer.cc +++ b/src/UI/DocumentViews/AudioVisualizer.cc @@ -6,19 +6,21 @@ using namespace Vivy; #define MAXPIXVALUE 7 // Some magix AV magic stuff AudioVisualizer::AudioVisualizer(AudioContext::StreamPtr stream, QWidget *parent) - : QWidget(parent) + : audioStream(stream), + QWidget(parent) { - if (!stream->isDecoded()) { - stream->decodeData(); + if (!audioStream->isDecoded()) { + qDebug() << "Need to decode data for stream" << audioStream->getStreamIndex(); + audioStream->decodeData(); } - double *decodedData = stream->getDecodedData(); + double *decodedData = audioStream->getDecodedData(); if (decodedData == nullptr) throw std::logic_error("the passed stream is not decoded"); - const size_t size = stream->getDecodedDataSize(); - const size_t height = stream->getDecodedChunkSize(); - const size_t decalage = stream->getDecodedDecalage(); + const size_t size = audioStream->getDecodedDataSize(); + const size_t height = audioStream->getDecodedChunkSize(); + const size_t decalage = audioStream->getDecodedDecalage(); const size_t width = (size - height) / decalage; uchar *pixels = new uchar[static_cast<size_t>(width * height / 2)](); @@ -59,14 +61,14 @@ 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, stream->getDuration()); + printSpectrum(img, audioStream); } void -AudioVisualizer::printSpectrum(QImage pixmap, quint64 audioLength) noexcept +AudioVisualizer::printSpectrum(QImage pixmap, AudioContext::StreamPtr stream) noexcept { - TimingView *timer = new TimingView(pixmap, audioLength, this); - TimingParams *params = new TimingParams(this); + TimingView *timer = new TimingView(pixmap, stream, this); + TimingParams *params= new TimingParams(this); // The only that we want to take all the space is the timer in itself QGridLayout *layout = new QGridLayout; diff --git a/src/UI/DocumentViews/AudioVisualizer.hh b/src/UI/DocumentViews/AudioVisualizer.hh index 82a1dad979eaf13a816ac87bd85a7c3c7a1f9603..65cbce7590932da689a0778fcea853d9690c4a2f 100644 --- a/src/UI/DocumentViews/AudioVisualizer.hh +++ b/src/UI/DocumentViews/AudioVisualizer.hh @@ -14,6 +14,9 @@ namespace Vivy class AudioVisualizer final : public QWidget { Q_OBJECT +private: + AudioContext::StreamPtr audioStream; + private: static constexpr inline auto fftSampleDeleter = [](FFTSample *ptr) noexcept -> void { if (ptr) @@ -35,7 +38,7 @@ public: ~AudioVisualizer() noexcept override = default; public slots: - void printSpectrum(QImage, quint64) noexcept; + void printSpectrum(QImage, AudioContext::StreamPtr) noexcept; }; } diff --git a/src/UI/DocumentViews/TimingAxis.cc b/src/UI/DocumentViews/TimingAxis.cc index d21b81144b13e092493d06032ebb987d87effad2..ca0ed613859d1a18f265dadbb45ea7f758825046 100644 --- a/src/UI/DocumentViews/TimingAxis.cc +++ b/src/UI/DocumentViews/TimingAxis.cc @@ -13,12 +13,12 @@ using namespace Vivy; -TimingAxis::TimingAxis(quint64 audioLength_, int x0_, int x1_, int y_) noexcept - : QGraphicsObject() - , audioLength(audioLength_) - , x0(x0_) - , x1(x1_) - , y(y_) +TimingAxis::TimingAxis(AudioContext::StreamPtr stream, int x0_, int x1_, int y_) noexcept + : QGraphicsObject(), + audioStream(stream), + x0(x0_), + x1(x1_), + y(y_) { } @@ -39,10 +39,9 @@ TimingAxis::refreshTicks() int nbAvailableTicks = availableTicks.size(); int minorTicksIndex = 0; QGraphicsScene *parentScene = scene(); - if (parentScene != nullptr) { - for (minorTicksIndex = 0; minorTicksIndex < nbAvailableTicks; minorTicksIndex++) { - if (parentScene->width() / (qreal(audioLength) * availableTicks[minorTicksIndex]) < - minBetweenMinor) { + if (parentScene != nullptr){ + for (minorTicksIndex = 0; minorTicksIndex < nbAvailableTicks; minorTicksIndex++){ + if (parentScene->width() / (qreal(audioStream->getLength()) * availableTicks[minorTicksIndex]) < minBetweenMinor){ minorTicksIndex = minorTicksIndex == 0 ? 0 : minorTicksIndex - 1; break; } @@ -58,10 +57,8 @@ TimingAxis::refreshTicks() } } -void -TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) -{ - if (audioLength == 0) +void TimingAxis::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { + if (audioStream->getLength() == 0) return; int ticks_base = y+ticksBase; diff --git a/src/UI/DocumentViews/TimingAxis.hh b/src/UI/DocumentViews/TimingAxis.hh index b045a0028a28dfce91b1090c4fc495df55a3aef6..d66c0c381114b86fd63077ff8def8750641b9cc7 100644 --- a/src/UI/DocumentViews/TimingAxis.hh +++ b/src/UI/DocumentViews/TimingAxis.hh @@ -9,6 +9,7 @@ #include <QPainter> #include <QGraphicsScene> #include <cmath> +#include "../../Lib/Audio.hh" namespace Vivy { @@ -16,8 +17,8 @@ class TimingAxis final : public QGraphicsObject { Q_OBJECT public: - explicit TimingAxis(quint64 audioLength, int x0, int x1, int y) noexcept; - ~TimingAxis() noexcept override = default; + explicit TimingAxis(AudioContext::StreamPtr stream, int x0, int x1, int y) noexcept; + ~TimingAxis() noexcept = default; QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; @@ -26,7 +27,7 @@ private: static inline constexpr QColor axisColour = QColor(0, 0, 127); qreal penWidth{ 1 }; - quint64 audioLength{ 0 }; + AudioContext::StreamPtr audioStream; /* * We use ints here because diff --git a/src/UI/DocumentViews/TimingScene.cc b/src/UI/DocumentViews/TimingScene.cc index a83996580a72b0f78c620f7f4f13db7e21d7bb6c..09372d8d96e420ed65352999d716a53419fdbaf7 100644 --- a/src/UI/DocumentViews/TimingScene.cc +++ b/src/UI/DocumentViews/TimingScene.cc @@ -16,13 +16,19 @@ using namespace Vivy; TimingScene::TimingScene(QImage img_, quint64 soundLength_, QWidget *parent) noexcept +TimingScene::TimingScene(QWidget *parent) noexcept + : QGraphicsScene(parent) +{ +} + +TimingScene::TimingScene(QImage img_, AudioContext::StreamPtr stream, QWidget *parent) noexcept : QGraphicsScene(parent) , img(img_) - , soundLength(soundLength_) + , audioStream(stream) { QPixmap pixmap(QPixmap::fromImage(img)); backgroundImg = addPixmap(pixmap); - ax = new TimingAxis(soundLength, 0, pixmap.width(), 10); + ax = new TimingAxis(stream, 0, pixmap.width(), 10); addItem(ax); ax->refreshTicks(); } @@ -83,7 +89,7 @@ TimingScene::timeFromPos(quint64 x) const qCritical() << "Try avoid possible divide by zero in the time from position"; return 0; } else { - return x * soundLength / w; + return x * audioStream->getLength() / w; } } diff --git a/src/UI/DocumentViews/TimingScene.hh b/src/UI/DocumentViews/TimingScene.hh index 3dc3f26cc0931d234032e0749e22a53483ddf0ec..5097a229b1e0436843184fb6c2d20e54adacf307 100644 --- a/src/UI/DocumentViews/TimingScene.hh +++ b/src/UI/DocumentViews/TimingScene.hh @@ -1,6 +1,7 @@ #pragma once #include "../../Lib/Utils.hh" +#include "../../Lib/Audio.hh" #include "../../Lib/Ass/Ass.hh" #include "TimingBar.hh" #include "TimingAxis.hh" @@ -18,13 +19,13 @@ public: static inline constexpr QColor startColour = QColor(127, 0, 127); static inline constexpr QColor endColour = QColor(0, 127, 0); - explicit TimingScene(QImage, quint64, QWidget * = nullptr) noexcept; - ~TimingScene() noexcept override = default; + explicit TimingScene(QWidget *parent = nullptr) noexcept; + explicit TimingScene(QImage, AudioContext::StreamPtr, QWidget * = nullptr) noexcept; private: QGraphicsPixmapItem *backgroundImg{ nullptr }; QImage img; - quint64 soundLength{ 0 }; + AudioContext::StreamPtr audioStream; Ass::LineWeakPtr currentLine{}; TimingMode timingMode{ TimingMode::Line }; TimingAxis *ax; diff --git a/src/UI/DocumentViews/TimingView.cc b/src/UI/DocumentViews/TimingView.cc index 81232d6f5fff31da7a4dacf56e235e879ae514fd..f40b27a6a3b0b820eda14c645234ff92a443fb30 100644 --- a/src/UI/DocumentViews/TimingView.cc +++ b/src/UI/DocumentViews/TimingView.cc @@ -2,10 +2,11 @@ using namespace Vivy; -TimingView::TimingView(QImage img, quint64 soundLength, QWidget *parent) noexcept +TimingView::TimingView(QImage img, AudioContext::StreamPtr stream, QWidget *parent) noexcept : QGraphicsView(parent) + , audioStream(stream) { - currentScene = new TimingScene(img, soundLength, this); + currentScene = new TimingScene(img, stream, this); setFixedHeight(img.height()); setMaximumHeight(img.height()); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); diff --git a/src/UI/DocumentViews/TimingView.hh b/src/UI/DocumentViews/TimingView.hh index e39ea90e4c13f2bcc140027305fb094d0c32d087..604d737ea16d2a0ea414acffd889f33cdd9dc129 100644 --- a/src/UI/DocumentViews/TimingView.hh +++ b/src/UI/DocumentViews/TimingView.hh @@ -5,6 +5,7 @@ #endif #include "../../Lib/Utils.hh" +#include "../../Lib/Audio.hh" #include "TimingBar.hh" #include "TimingScene.hh" #include "TimingAxis.hh" @@ -20,14 +21,17 @@ 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; - ~TimingView() noexcept override = default; + explicit TimingView(QImage, AudioContext::StreamPtr, QWidget * = nullptr) noexcept; + ~TimingView() noexcept = default; + + TimingScene* getTimingScene() const; TimingScene *getTimingScene() const; void wheelEvent(QWheelEvent *) noexcept override; private: TimingScene *currentScene{ nullptr }; + AudioContext::StreamPtr audioStream; public slots: void mousePressEvent(QMouseEvent *event) noexcept override; diff --git a/src/UI/VivyDocumentView.cc b/src/UI/VivyDocumentView.cc index 86e155afd60eac27964e0f270e8dba6f6d8ff4f8..4fb0a4307404179047f79c8416392f9760075e4d 100644 --- a/src/UI/VivyDocumentView.cc +++ b/src/UI/VivyDocumentView.cc @@ -7,6 +7,14 @@ #include "../VivyApplication.hh" #include "../Lib/Document/VivyDocument.hh" #include "../Lib/Document/VivyDocumentStore.hh" +#include "../Lib/Audio.hh" + +#include <QHeaderView> +#include <QTreeView> +#include <QVBoxLayout> +#include <QTableView> +#include <QWidget> +#include <QDockWidget> using namespace Vivy; @@ -118,15 +126,31 @@ VivyDocumentView::loadAssView() noexcept void VivyDocumentView::loadAudioView() noexcept { - if (!document->checkDocumentCapabilities(VivyDocument::AudioAble)) - return; - - std::shared_ptr<AudioSubDocument> audioDocument = document->getAudioSubDocument(); - AudioContext::StreamPtr stream = audioDocument->getDefaultStream(); - - if (stream == nullptr) { - logError() << "Failed to get default audio stream"; - return; + if (document->checkDocumentCapabilities(VivyDocument::AudioAble)) { + AudioContext::StreamPtr stream = document->getAudioStream(); + if (stream == nullptr) { + qCritical() << "Failed to get default audio stream"; + return; + } + + if (!visualizer) { + visualizer = new QDockWidget("Visualizer", this); + + visualizer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); + visualizer->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::TopDockWidgetArea | + Qt::BottomDockWidgetArea); + visualizer->setFeatures(QDockWidget::DockWidgetMovable | + QDockWidget::DockWidgetClosable); + addDockWidget(Qt::LeftDockWidgetArea, visualizer, Qt::Horizontal); + visualizer->setTitleBarWidget(new QWidget(this)); + Utils::setTransparentBackgroundForWidget(visualizer->titleBarWidget()); + } + + // Kubat: don't check, may throw an error but don't think we can + // recover from it. + AudioVisualizer *visualizerInner = new AudioVisualizer(stream, visualizer); + visualizer->setWidget(visualizerInner); + visualizer->layout()->setAlignment(visualizerInner, Qt::AlignTop); } if (!visualizer) {