summaryrefslogtreecommitdiffstats
path: root/src/qml/qmlwaveformoverview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qmlwaveformoverview.cpp')
-rw-r--r--src/qml/qmlwaveformoverview.cpp267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/qml/qmlwaveformoverview.cpp b/src/qml/qmlwaveformoverview.cpp
new file mode 100644
index 0000000000..23d799224f
--- /dev/null
+++ b/src/qml/qmlwaveformoverview.cpp
@@ -0,0 +1,267 @@
+#include "qml/qmlwaveformoverview.h"
+
+#include "mixer/basetrackplayer.h"
+#include "qml/qmlplayerproxy.h"
+
+namespace {
+constexpr double kDesiredChannelHeight = 255;
+} // namespace
+
+namespace mixxx {
+namespace skin {
+namespace qml {
+
+QmlWaveformOverview::QmlWaveformOverview(QQuickItem* parent)
+ : QQuickPaintedItem(parent),
+ m_pPlayer(nullptr),
+ m_channels(ChannelFlag::BothChannels),
+ m_renderer(Renderer::RGB),
+ m_colorHigh(0xFF0000),
+ m_colorMid(0x00FF00),
+ m_colorLow(0x0000FF) {
+}
+
+QmlPlayerProxy* QmlWaveformOverview::getPlayer() const {
+ return m_pPlayer;
+}
+
+void QmlWaveformOverview::setPlayer(QmlPlayerProxy* pPlayer) {
+ if (m_pPlayer == pPlayer) {
+ return;
+ }
+
+ if (m_pPlayer != nullptr) {
+ m_pPlayer->internalTrackPlayer()->disconnect(this);
+ }
+
+ m_pPlayer = pPlayer;
+
+ if (pPlayer != nullptr) {
+ connect(m_pPlayer->internalTrackPlayer(),
+ &BaseTrackPlayer::newTrackLoaded,
+ this,
+ &QmlWaveformOverview::slotTrackLoaded);
+ connect(m_pPlayer->internalTrackPlayer(),
+ &BaseTrackPlayer::loadingTrack,
+ this,
+ &QmlWaveformOverview::slotTrackLoading);
+ connect(m_pPlayer->internalTrackPlayer(),
+ &BaseTrackPlayer::playerEmpty,
+ this,
+ &QmlWaveformOverview::slotTrackUnloaded);
+ }
+
+ emit playerChanged();
+ update();
+}
+
+QmlWaveformOverview::Channels QmlWaveformOverview::getChannels() const {
+ return m_channels;
+}
+
+void QmlWaveformOverview::setChannels(QmlWaveformOverview::Channels channels) {
+ if (m_channels == channels) {
+ return;
+ }
+
+ m_channels = channels;
+ emit channelsChanged(channels);
+}
+
+void QmlWaveformOverview::slotTrackLoaded(TrackPointer pTrack) {
+ // TODO: Investigate if it's a bug that this debug assertion fails when
+ // passing tracks on the command line
+ // DEBUG_ASSERT(m_pCurrentTrack == pTrack);
+ setCurrentTrack(pTrack);
+}
+
+void QmlWaveformOverview::slotTrackLoading(TrackPointer pNewTrack, TrackPointer pOldTrack) {
+ Q_UNUSED(pOldTrack); // only used in DEBUG_ASSERT
+ DEBUG_ASSERT(m_pCurrentTrack == pOldTrack);
+ setCurrentTrack(pNewTrack);
+}
+
+void QmlWaveformOverview::slotTrackUnloaded() {
+ setCurrentTrack(nullptr);
+}
+
+void QmlWaveformOverview::setCurrentTrack(TrackPointer pTrack) {
+ // TODO: Check if this is actually possible
+ if (m_pCurrentTrack == pTrack) {
+ return;
+ }
+
+ if (m_pCurrentTrack != nullptr) {
+ disconnect(m_pCurrentTrack.get(), nullptr, this, nullptr);
+ }
+
+ m_pCurrentTrack = pTrack;
+ if (pTrack != nullptr) {
+ connect(pTrack.get(),
+ &Track::waveformSummaryUpdated,
+ this,
+ &QmlWaveformOverview::slotWaveformUpdated);
+ }
+ slotWaveformUpdated();
+}
+
+void QmlWaveformOverview::slotWaveformUpdated() {
+ update();
+}
+
+void QmlWaveformOverview::paint(QPainter* pPainter) {
+ TrackPointer pTrack = m_pCurrentTrack;
+ if (!pTrack) {
+ return;
+ }
+
+ ConstWaveformPointer pWaveform = pTrack->getWaveformSummary();
+ if (!pWaveform) {
+ return;
+ }
+
+ const int dataSize = pWaveform->getDataSize();
+ if (dataSize == 0) {
+ return;
+ }
+
+ constexpr int actualCompletion = 0;
+ // Always multiple of 2
+ const int waveformCompletion = pWaveform->getCompletion();
+ // Test if there is some new to draw (at least of pixel width)
+ const int completionIncrement = waveformCompletion - actualCompletion;
+
+ const qreal desiredWidth = static_cast<qreal>(dataSize) / 2;
+ const double visiblePixelIncrement = completionIncrement * desiredWidth / dataSize;
+ if (waveformCompletion < (dataSize - 2) &&
+ (completionIncrement < 2 || visiblePixelIncrement == 0)) {
+ return;
+ }
+
+ const int nextCompletion = actualCompletion + completionIncrement;
+
+ const Channels channels = m_channels;
+ pPainter->save();
+
+ switch (channels) {
+ case static_cast<int>(ChannelFlag::LeftChannel):
+ // Draw both channels.
+ // Set the y axis to half the height of the item
+ pPainter->translate(0.0, height());
+ // Set the x axis to half the height of the item
+ pPainter->scale(width() / desiredWidth, height() / kDesiredChannelHeight);
+ break;
+ case static_cast<int>(ChannelFlag::RightChannel):
+ // Set the x axis to half the height of the item
+ pPainter->scale(width() / desiredWidth, height() / kDesiredChannelHeight);
+ break;
+ default:
+ // Draw both channels.
+ // Set the y axis to half the height of the item
+ pPainter->translate(0.0, height() / 2);
+ // Set the x axis to half the height of the item
+ pPainter->scale(width() / desiredWidth, height() / (2 * kDesiredChannelHeight));
+ }
+
+ Renderer renderer = m_renderer;
+ for (int currentCompletion = actualCompletion;
+ currentCompletion < nextCompletion;
+ currentCompletion += 2) {
+ switch (renderer) {
+ case Renderer::Filtered:
+ drawFiltered(pPainter, channels, pWaveform, currentCompletion);
+ break;
+ default:
+ drawRgb(pPainter, channels, pWaveform, currentCompletion);
+ }
+ }
+ pPainter->restore();
+}
+
+void QmlWaveformOverview::drawRgb(QPainter* pPainter,
+ Channels channels,
+ ConstWaveformPointer pWaveform,
+ int completion) const {
+ const double offsetX = completion / 2.0;
+
+ if (channels.testFlag(ChannelFlag::LeftChannel)) {
+ // Draw left channel
+ const QColor leftColor = getRgbPenColor(pWaveform, completion);
+ if (leftColor.isValid()) {
+ const uint8_t leftValue = pWaveform->getAll(completion);
+ pPainter->setPen(leftColor);
+ pPainter->drawLine(QPointF(offsetX, -leftValue), QPointF(offsetX, 0.0));
+ }
+ }
+
+ if (channels.testFlag(ChannelFlag::RightChannel)) {
+ // Draw right channel
+ QColor rightColor = getRgbPenColor(pWaveform, completion + 1);
+ if (rightColor.isValid()) {
+ const uint8_t rightValue = pWaveform->getAll(completion + 1);
+ pPainter->setPen(rightColor);
+ pPainter->drawLine(QPointF(offsetX, 0.0), QPointF(offsetX, rightValue));
+ }
+ }
+}
+
+void QmlWaveformOverview::drawFiltered(QPainter* pPainter,
+ Channels channels,
+ ConstWaveformPointer pWaveform,
+ int completion) const {
+ const double offsetX = completion / 2.0;
+
+ if (channels.testFlag(ChannelFlag::LeftChannel)) {
+ const uint8_t leftHigh = pWaveform->getHigh(completion);
+ pPainter->setPen(m_colorHigh);
+ pPainter->drawLine(QPointF(offsetX, 2 * -leftHigh), QPointF(offsetX, 0.0));
+
+ const uint8_t leftMid = pWaveform->getMid(completion);
+ pPainter->setPen(m_colorMid);
+ pPainter->drawLine(QPointF(offsetX, 1.5 * -leftMid), QPointF(offsetX, 0.0));
+
+ const uint8_t leftLow = pWaveform->getLow(completion);
+ pPainter->setPen(m_colorLow);
+ pPainter->drawLine(QPointF(offsetX, -leftLow), QPointF(offsetX, 0.0));
+ }
+
+ if (channels.testFlag(ChannelFlag::RightChannel)) {
+ const uint8_t rightHigh = pWaveform->getHigh(completion + 1);
+ pPainter->setPen(m_colorHigh);
+ pPainter->drawLine(QPointF(offsetX, 0), QPointF(offsetX, 2 * rightHigh));
+
+ const uint8_t rightMid = pWaveform->getMid(completion + 1) * 2;
+ pPainter->setPen(m_colorMid);
+ pPainter->drawLine(QPointF(offsetX, 0), QPointF(offsetX, 1.5 * rightMid));
+
+ const uint8_t rightLow = pWaveform->getLow(completion + 1);
+ pPainter->setPen(m_colorLow);
+ pPainter->drawLine(QPointF(offsetX, 0), QPointF(offsetX, rightLow));
+ }
+}
+
+QColor QmlWaveformOverview::getRgbPenColor(ConstWaveformPointer pWaveform, int completion) const {
+ // Retrieve "raw" LMH values from waveform
+ qreal low = static_cast<qreal>(pWaveform->getLow(completion));
+ qreal mid = static_cast<qreal>(pWaveform->getMid(completion));
+ qreal high = static_cast<qreal>(pWaveform->getHigh(completion));
+
+ // Do matrix multiplication
+ qreal red = low * m_colorLow.redF() + mid * m_colorMid.redF() + high * m_colorHigh.redF();
+ qreal green = low * m_colorLow.greenF() + mid * m_colorMid.greenF() +
+ high * m_colorHigh.greenF();
+ qreal blue = low * m_colorLow.blueF() + mid * m_colorMid.blueF() + high * m_colorHigh.blueF();
+
+ // Normalize and draw
+ qreal max = math_max3(red, green, blue);
+ if (max > 0.0) {
+ QColor color;
+ color.setRgbF(red / max, green / max, blue / max);
+ return color;
+ }
+ return QColor();
+}
+
+} // namespace qml
+} // namespace skin
+} // namespace mixxx