From 8a586273d604cdc54be4fca8be9a1728593f0567 Mon Sep 17 00:00:00 2001 From: Antoine C Date: Mon, 25 Mar 2024 19:55:14 +0000 Subject: [PATCH] WIP - SlipMode waveform visual --- CMakeLists.txt | 2 + src/shaders/slipmodeshader.cpp | 49 ++++++++ src/shaders/slipmodeshader.h | 31 +++++ .../allshader/waveformrenderbeat.cpp | 39 +++++- .../allshader/waveformrendererrgb.cpp | 119 +++++++++++------- .../allshader/waveformrendererslipmode.cpp | 97 ++++++++++++++ .../allshader/waveformrendererslipmode.h | 41 ++++++ .../allshader/waveformrendermark.cpp | 14 ++- .../allshader/waveformrendermarkrange.cpp | 15 ++- .../renderers/waveformwidgetrenderer.cpp | 16 ++- .../renderers/waveformwidgetrenderer.h | 26 +++- .../widgets/allshader/rgbwaveformwidget.cpp | 2 + 12 files changed, 394 insertions(+), 57 deletions(-) create mode 100644 src/shaders/slipmodeshader.cpp create mode 100644 src/shaders/slipmodeshader.h create mode 100644 src/waveform/renderers/allshader/waveformrendererslipmode.cpp create mode 100644 src/waveform/renderers/allshader/waveformrendererslipmode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c67775d2566..6f1f14cb286f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1497,6 +1497,7 @@ endif() if(QOPENGL) target_sources(mixxx-lib PRIVATE src/shaders/endoftrackshader.cpp + src/shaders/slipmodeshader.cpp src/shaders/patternshader.cpp src/shaders/rgbashader.cpp src/shaders/rgbshader.cpp @@ -1510,6 +1511,7 @@ if(QOPENGL) src/waveform/renderers/allshader/waveformrenderbeat.cpp src/waveform/renderers/allshader/waveformrenderer.cpp src/waveform/renderers/allshader/waveformrendererendoftrack.cpp + src/waveform/renderers/allshader/waveformrendererslipmode.cpp src/waveform/renderers/allshader/waveformrendererfiltered.cpp src/waveform/renderers/allshader/waveformrendererhsv.cpp src/waveform/renderers/allshader/waveformrendererlrrgb.cpp diff --git a/src/shaders/slipmodeshader.cpp b/src/shaders/slipmodeshader.cpp new file mode 100644 index 000000000000..0a1a6601c890 --- /dev/null +++ b/src/shaders/slipmodeshader.cpp @@ -0,0 +1,49 @@ +#include "shaders/slipmodeshader.h" + +using namespace mixxx; + +void SlipModeShader::init() { + QString vertexShaderCode = QStringLiteral(R"--( +#version 150 +attribute highp vec4 position; // use vec4 here (will be padded) to assign directly to gl_Position + +out highp vec4 vposition; + +void main() +{ + vposition = position; + gl_Position = position; +} +)--"); + + QString fragmentShaderCode = QStringLiteral(R"--( +#version 120 +uniform highp vec4 color; +uniform highp vec2 dimension; + +in highp vec4 vposition; + +void main() +{ + + float xBorder = abs(dimension.x*vposition.x); + float yBorder = dimension.y*vposition.y; + + gl_FragColor = vec4(0, 0, 0, 0); + if( (xBorder > dimension.x - 10 && yBorder >= 0) || yBorder < 0 || yBorder > dimension.y - 10) + { + float borderAlpha = max( + yBorder < 0 ? 0 : max(0, xBorder - dimension.x + 10), + yBorder < 0 ? max(0, 10 + yBorder) : max(0, yBorder - dimension.y + 10) + ) / 10; + gl_FragColor = vec4(color.xyz, min(color.w, mix(0, 1, borderAlpha))); + } +} +)--"); + + load(vertexShaderCode, fragmentShaderCode); + + m_positionLocation = attributeLocation("position"); + m_dimensionLocation = uniformLocation("dimension"); + m_colorLocation = uniformLocation("color"); +} diff --git a/src/shaders/slipmodeshader.h b/src/shaders/slipmodeshader.h new file mode 100644 index 000000000000..6cc4a6a047dc --- /dev/null +++ b/src/shaders/slipmodeshader.h @@ -0,0 +1,31 @@ +#pragma once + +#include "shaders/shader.h" + +namespace mixxx { +class SlipModeShader; +} + +class mixxx::SlipModeShader : public mixxx::Shader { + public: + SlipModeShader() = default; + ~SlipModeShader() = default; + void init(); + + int positionLocation() const { + return m_positionLocation; + } + int dimensionLocation() const { + return m_dimensionLocation; + } + int colorLocation() const { + return m_colorLocation; + } + + private: + int m_positionLocation; + int m_dimensionLocation; + int m_colorLocation; + + DISALLOW_COPY_AND_ASSIGN(SlipModeShader) +}; diff --git a/src/waveform/renderers/allshader/waveformrenderbeat.cpp b/src/waveform/renderers/allshader/waveformrenderbeat.cpp index 9db24bcbb6a3..a488e7a1d807 100644 --- a/src/waveform/renderers/allshader/waveformrenderbeat.cpp +++ b/src/waveform/renderers/allshader/waveformrenderbeat.cpp @@ -57,11 +57,19 @@ void WaveformRenderBeat::paintGL() { m_waveformRenderer->getFirstDisplayedPosition(); const double lastDisplayedPosition = m_waveformRenderer->getLastDisplayedPosition(); + const double firstSlipDisplayedPosition = + m_waveformRenderer->getDisplayedSlipPosition(); + const double lastSlipDisplayedPosition = + firstSlipDisplayedPosition + (lastDisplayedPosition - firstDisplayedPosition); const auto startPosition = mixxx::audio::FramePos::fromEngineSamplePos( firstDisplayedPosition * trackSamples); const auto endPosition = mixxx::audio::FramePos::fromEngineSamplePos( lastDisplayedPosition * trackSamples); + const auto startSlipPosition = mixxx::audio::FramePos::fromEngineSamplePos( + firstSlipDisplayedPosition * trackSamples); + const auto endSlipPosition = mixxx::audio::FramePos::fromEngineSamplePos( + lastSlipDisplayedPosition * trackSamples); if (!startPosition.isValid() || !endPosition.isValid()) { return; @@ -81,11 +89,40 @@ void WaveformRenderBeat::paintGL() { ++it) { numBeatsInRange++; } + if (firstSlipDisplayedPosition != firstDisplayedPosition) { + for (auto it = trackBeats->iteratorFrom(startSlipPosition); + it != trackBeats->cend() && *it <= endSlipPosition; + ++it) { + numBeatsInRange++; + } + } const int reserved = numBeatsInRange * numVerticesPerLine; m_vertices.clear(); m_vertices.reserve(reserved); + if (firstSlipDisplayedPosition != firstDisplayedPosition) { + for (auto it = trackBeats->iteratorFrom(startSlipPosition); + it != trackBeats->cend() && *it <= endSlipPosition; + ++it) { + double beatPosition = it->toEngineSamplePos(); + double xBeatPoint = + m_waveformRenderer->transformSampleSlipPositionInRendererWorld(beatPosition); + + xBeatPoint = qRound(xBeatPoint * devicePixelRatio) / devicePixelRatio; + + const float x1 = static_cast(xBeatPoint); + const float x2 = x1 + 1.f; + + m_vertices.addRectangle(x1, 0.f, x2, rendererBreadth / 2.f); + } + } + + const float originRendererBreadth = + firstSlipDisplayedPosition != firstDisplayedPosition + ? rendererBreadth / 2.f + : 0.f; + for (auto it = trackBeats->iteratorFrom(startPosition); it != trackBeats->cend() && *it <= endPosition; ++it) { @@ -98,7 +135,7 @@ void WaveformRenderBeat::paintGL() { const float x1 = static_cast(xBeatPoint); const float x2 = x1 + 1.f; - m_vertices.addRectangle(x1, 0.f, x2, rendererBreadth); + m_vertices.addRectangle(x1, originRendererBreadth, x2, rendererBreadth); } DEBUG_ASSERT(reserved == m_vertices.size()); diff --git a/src/waveform/renderers/allshader/waveformrendererrgb.cpp b/src/waveform/renderers/allshader/waveformrendererrgb.cpp index a1c18a6c58a6..b3dde9b32b38 100644 --- a/src/waveform/renderers/allshader/waveformrendererrgb.cpp +++ b/src/waveform/renderers/allshader/waveformrendererrgb.cpp @@ -58,6 +58,8 @@ void WaveformRendererRGB::paintGL() { m_waveformRenderer->getFirstDisplayedPosition() * visualFramesSize; const double lastVisualFrame = m_waveformRenderer->getLastDisplayedPosition() * visualFramesSize; + const double slipFirstVisualIndex = + m_waveformRenderer->getDisplayedSlipPosition() * visualFramesSize; // Represents the # of visual frames per horizontal pixel. const double visualIncrementPerPixel = @@ -86,10 +88,14 @@ void WaveformRendererRGB::paintGL() { // Effective visual frame for x double xVisualFrame = qRound(firstVisualFrame / visualIncrementPerPixel) * visualIncrementPerPixel; + double xSlipVisualOffset = + qRound((slipFirstVisualIndex - firstVisualFrame) / + visualIncrementPerPixel) * + visualIncrementPerPixel; const int numVerticesPerLine = 6; // 2 triangles - const int reserved = numVerticesPerLine * (length + 1); + const int reserved = numVerticesPerLine * (2 * length + 1); m_vertices.clear(); m_vertices.reserve(reserved); @@ -107,6 +113,8 @@ void WaveformRendererRGB::paintGL() { const double maxSamplingRange = visualIncrementPerPixel / 2.0; + const int slipOffset = std::lround(xSlipVisualOffset) * 2; + for (int pos = 0; pos < length; ++pos) { const int visualFrameStart = std::lround(xVisualFrame - maxSamplingRange); const int visualFrameStop = std::lround(xVisualFrame + maxSamplingRange); @@ -119,79 +127,96 @@ void WaveformRendererRGB::paintGL() { // Find the max values for low, mid, high and all in the waveform data. // - Max of left and right - uchar u8maxLow{}; - uchar u8maxMid{}; - uchar u8maxHigh{}; + uchar u8maxLow[2]{}; + uchar u8maxMid[2]{}; + uchar u8maxHigh[2]{}; // - Per channel uchar u8maxAllChn[2]{}; - for (int chn = 0; chn < 2; chn++) { - // data is interleaved left / right - for (int i = visualIndexStart + chn; i < visualIndexStop + chn; i += 2) { - const WaveformData& waveformData = data[i]; - - u8maxLow = math_max(u8maxLow, waveformData.filtered.low); - u8maxMid = math_max(u8maxMid, waveformData.filtered.mid); - u8maxHigh = math_max(u8maxHigh, waveformData.filtered.high); - u8maxAllChn[chn] = math_max(u8maxAllChn[chn], waveformData.filtered.all); - } + // for (int chn = 0; chn < 2; chn++) { + // data is interleaved left / right + for (int i = visualIndexStart; i < visualIndexStop; i += 2) { + const WaveformData& waveformData = data[i + slipOffset]; + + u8maxLow[0] = math_max(u8maxLow[0], waveformData.filtered.low); + u8maxMid[0] = math_max(u8maxMid[0], waveformData.filtered.mid); + u8maxHigh[0] = math_max(u8maxHigh[0], waveformData.filtered.high); + u8maxAllChn[0] = math_max(u8maxAllChn[0], waveformData.filtered.all); + + u8maxLow[1] = math_max(u8maxLow[1], data[i + 1].filtered.low); + u8maxMid[1] = math_max(u8maxMid[1], data[i + 1].filtered.mid); + u8maxHigh[1] = math_max(u8maxHigh[1], data[i + 1].filtered.high); + u8maxAllChn[1] = math_max(u8maxAllChn[1], data[i + 1].filtered.all); } + // } // Cast to float - float maxLow = static_cast(u8maxLow); - float maxMid = static_cast(u8maxMid); - float maxHigh = static_cast(u8maxHigh); + float maxLow[2]{static_cast(u8maxLow[0]), static_cast(u8maxLow[1])}; + float maxMid[2]{static_cast(u8maxMid[0]), static_cast(u8maxMid[1])}; + float maxHigh[2]{static_cast(u8maxHigh[0]), static_cast(u8maxHigh[1])}; float maxAllChn[2]{static_cast(u8maxAllChn[0]), static_cast(u8maxAllChn[1])}; // Uncomment to undo scaling with pow(value, 2.0f * 0.316f) done in analyzerwaveform.h // float maxAllChn[2]{unscale(u8maxAllChn[0]), unscale(u8maxAllChn[1])}; // Calculate the squared magnitude of the maxLow, maxMid and maxHigh values. // We take the square root to get the magnitude below. - const float sum = math_pow2(maxLow) + math_pow2(maxMid) + math_pow2(maxHigh); + const float sum[2]{ + math_pow2(maxLow[0]) + math_pow2(maxMid[0]) + math_pow2(maxHigh[0]), + math_pow2(maxLow[1]) + math_pow2(maxMid[1]) + math_pow2(maxHigh[1])}; // Apply the gains - maxLow *= lowGain; - maxMid *= midGain; - maxHigh *= highGain; + for (int chn = 0; chn < 2; chn++) { + maxLow[chn] *= lowGain; + maxMid[chn] *= midGain; + maxHigh[chn] *= highGain; + } // Calculate the squared magnitude of the gained maxLow, maxMid and maxHigh values // We take the square root to get the magnitude below. - const float sumGained = math_pow2(maxLow) + math_pow2(maxMid) + math_pow2(maxHigh); + const float sumGained[2]{ + math_pow2(maxLow[0]) + math_pow2(maxMid[0]) + math_pow2(maxHigh[0]), + math_pow2(maxLow[1]) + math_pow2(maxMid[1]) + math_pow2(maxHigh[1])}; // The maxAll values will be used to draw the amplitude. We scale them according to // magnitude of the gained maxLow, maxMid and maxHigh values - if (sum != 0.f) { - // magnitude = sqrt(sum) and magnitudeGained = sqrt(sumGained), and - // factor = magnitudeGained / magnitude, but we can do with a single sqrt: - const float factor = std::sqrt(sumGained / sum); - maxAllChn[0] *= factor; - maxAllChn[1] *= factor; - } + for (int chn = 0; chn < 2; chn++) { + if (sum[chn] != 0.f) { + // magnitude = sqrt(sum) and magnitudeGained = sqrt(sumGained), and + // factor = magnitudeGained / magnitude, but we can do with a single sqrt: + const float factor = std::sqrt(sumGained[chn] / sum[chn]); + maxAllChn[0] *= factor; + maxAllChn[1] *= factor; + } - // Use the gained maxLow, maxMid and maxHigh values to calculate the color components - float red = maxLow * low_r + maxMid * mid_r + maxHigh * high_r; - float green = maxLow * low_g + maxMid * mid_g + maxHigh * high_g; - float blue = maxLow * low_b + maxMid * mid_b + maxHigh * high_b; - - // Normalize the color components using the maximum of the three - const float maxComponent = math_max3(red, green, blue); - if (maxComponent == 0.f) { - // Avoid division by 0 - red = 0.f; - green = 0.f; - blue = 0.f; - } else { - const float normFactor = 1.f / maxComponent; - red *= normFactor; - green *= normFactor; - blue *= normFactor; + // Use the gained maxLow, maxMid and maxHigh values to calculate the color components + float red = maxLow[chn] * low_r + maxMid[chn] * mid_r + maxHigh[chn] * high_r; + float green = maxLow[chn] * low_g + maxMid[chn] * mid_g + maxHigh[chn] * high_g; + float blue = maxLow[chn] * low_b + maxMid[chn] * mid_b + maxHigh[chn] * high_b; + + // Normalize the color components using the maximum of the three + const float maxComponent = math_max3(red, green, blue); + if (maxComponent == 0.f) { + // Avoid division by 0 + red = 0.f; + green = 0.f; + blue = 0.f; + } else { + const float normFactor = 1.f / maxComponent; + red *= normFactor; + green *= normFactor; + blue *= normFactor; + } + m_colors.addForRectangle(red, green, blue); } // Lines are thin rectangles m_vertices.addRectangle(fpos - 0.5f, halfBreadth - heightFactor * maxAllChn[0], fpos + 0.5f, + halfBreadth); + m_vertices.addRectangle(fpos - 0.5f, + halfBreadth, + fpos + 0.5f, halfBreadth + heightFactor * maxAllChn[1]); - m_colors.addForRectangle(red, green, blue); xVisualFrame += visualIncrementPerPixel; } diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.cpp b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp new file mode 100644 index 000000000000..4972bb57435c --- /dev/null +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.cpp @@ -0,0 +1,97 @@ +#include "waveform/renderers/allshader/waveformrendererslipmode.h" + +#include +#include + +#include "control/controlproxy.h" +#include "waveform/renderers/waveformwidgetrenderer.h" +#include "waveform/waveformwidgetfactory.h" +#include "widget/wskincolor.h" + +namespace { + +constexpr int kBlinkingPeriodMillis = 1600; +constexpr float positionArray[] = {-1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; + +} // anonymous namespace + +namespace allshader { + +WaveformRendererSlipMode::WaveformRendererSlipMode( + WaveformWidgetRenderer* waveformWidget) + : WaveformRenderer(waveformWidget), + m_pSlipMode(nullptr) { +} + +bool WaveformRendererSlipMode::init() { + m_timer.restart(); + + m_pSlipMode.reset(new ControlProxy( + m_waveformRenderer->getGroup(), "slip_enabled")); + + return true; +} + +void WaveformRendererSlipMode::setup(const QDomNode& node, const SkinContext& context) { + m_color = QColor(20, 200, 20); + const QString slipModeOutlineColorName = context.selectString(node, "SlipModeOutlineColor"); + if (!slipModeOutlineColorName.isNull()) { + m_color = QColor(slipModeOutlineColorName); + m_color = WSkinColor::getCorrectColor(m_color); + } +} + +void WaveformRendererSlipMode::initializeGL() { + WaveformRenderer::initializeGL(); + m_shader.init(); +} + +void WaveformRendererSlipMode::fillWithGradient(QColor color) { +} + +void WaveformRendererSlipMode::paintGL() { + auto firstPlayedFrame = m_waveformRenderer->getFirstDisplayedPosition(); + auto firstSlipFrame = m_waveformRenderer->getDisplayedSlipPosition(); + + if (!m_pSlipMode->toBool() || firstSlipFrame == firstPlayedFrame) { + return; + } + + const int elapsed = m_timer.elapsed().toIntegerMillis() % kBlinkingPeriodMillis; + + const double blinkIntensity = (double)(2 * abs(elapsed - kBlinkingPeriodMillis / 2)) / + kBlinkingPeriodMillis; + + const double alpha = 0.5 + 0.5 * blinkIntensity; + + if (alpha != 0.0) { + QColor color = m_color; + color.setAlphaF(static_cast(alpha)); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + const int colorLocation = m_shader.colorLocation(); + const int positionLocation = m_shader.positionLocation(); + const int gradientLocation = m_shader.dimensionLocation(); + + m_shader.bind(); + m_shader.enableAttributeArray(positionLocation); + + m_shader.setUniformValue(colorLocation, color); + + m_shader.setAttributeArray( + positionLocation, GL_FLOAT, positionArray, 2); + + m_shader.setUniformValue(gradientLocation, + (float)m_waveformRenderer->getLength() / 2, + (float)m_waveformRenderer->getBreadth() / 2); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + m_shader.disableAttributeArray(positionLocation); + m_shader.release(); + } +} + +} // namespace allshader diff --git a/src/waveform/renderers/allshader/waveformrendererslipmode.h b/src/waveform/renderers/allshader/waveformrendererslipmode.h new file mode 100644 index 000000000000..b8df3b67a864 --- /dev/null +++ b/src/waveform/renderers/allshader/waveformrendererslipmode.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +#include "shaders/slipmodeshader.h" +#include "util/class.h" +#include "util/performancetimer.h" +#include "waveform/renderers/allshader/waveformrenderer.h" + +class ControlProxy; +class QDomNode; +class SkinContext; + +namespace allshader { +class WaveformRendererSlipMode; +} + +class allshader::WaveformRendererSlipMode final : public allshader::WaveformRenderer { + public: + explicit WaveformRendererSlipMode( + WaveformWidgetRenderer* waveformWidget); + + void setup(const QDomNode& node, const SkinContext& context) override; + + bool init() override; + + void initializeGL() override; + void paintGL() override; + + private: + void fillWithGradient(QColor color); + + mixxx::SlipModeShader m_shader; + std::unique_ptr m_pSlipMode; + + QColor m_color; + PerformanceTimer m_timer; + + DISALLOW_COPY_AND_ASSIGN(WaveformRendererSlipMode); +}; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index fc5f7004bf9c..ec20154db582 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -162,6 +162,16 @@ void allshader::WaveformRenderMark::paintGL() { // Will create textures so requires OpenGL context updateMarkImages(); + const double firstDisplayedPosition = + m_waveformRenderer->getFirstDisplayedPosition(); + const double firstSlipDisplayedPosition = + m_waveformRenderer->getDisplayedSlipPosition(); + + const float originRendererBreadth = + firstSlipDisplayedPosition != firstDisplayedPosition + ? m_waveformRenderer->getBreadth() / 2.f + : 0.f; + for (const auto& pMark : std::as_const(m_marks)) { QOpenGLTexture* pTexture = static_cast(pMark->m_pGraphics.get()) @@ -186,7 +196,7 @@ void allshader::WaveformRenderMark::paintGL() { if (currentMarkPoint > -markHalfWidth && currentMarkPoint < m_waveformRenderer->getLength() + markHalfWidth) { - drawTexture(drawOffset, 0, pTexture); + drawTexture(drawOffset, originRendererBreadth, pTexture); visible = true; } @@ -204,7 +214,7 @@ void allshader::WaveformRenderMark::paintGL() { color.setAlphaF(0.4f); drawMark( - QRectF(QPointF(currentMarkPoint, 0), + QRectF(QPointF(currentMarkPoint, originRendererBreadth), QPointF(currentMarkEndPoint, m_waveformRenderer ->getBreadth())), diff --git a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp index 6b3a33d49360..bdd19b9bd018 100644 --- a/src/waveform/renderers/allshader/waveformrendermarkrange.cpp +++ b/src/waveform/renderers/allshader/waveformrendermarkrange.cpp @@ -59,12 +59,21 @@ void allshader::WaveformRenderMarkRange::paintGL() { const int positionLocation = m_shader.positionLocation(); const int matrixLocation = m_shader.matrixLocation(); + const double firstDisplayedPosition = + m_waveformRenderer->getFirstDisplayedPosition(); + const double firstSlipDisplayedPosition = + m_waveformRenderer->getDisplayedSlipPosition(); m_shader.bind(); m_shader.enableAttributeArray(positionLocation); m_shader.setUniformValue(matrixLocation, matrix); + const float originRendererBreadth = + firstSlipDisplayedPosition != firstDisplayedPosition + ? m_waveformRenderer->getBreadth() / 2.f + : 0.f; + for (auto&& markRange : m_markRanges) { // If the mark range is not active we should not draw it. if (!markRange.active()) { @@ -99,7 +108,11 @@ void allshader::WaveformRenderMarkRange::paintGL() { QColor color = markRange.enabled() ? markRange.m_activeColor : markRange.m_disabledColor; color.setAlphaF(0.3f); - fillRect(QRectF(startPosition, 0, span, m_waveformRenderer->getBreadth()), color); + fillRect(QRectF(startPosition, + originRendererBreadth, + span, + m_waveformRenderer->getBreadth()), + color); } m_shader.disableAttributeArray(positionLocation); m_shader.release(); diff --git a/src/waveform/renderers/waveformwidgetrenderer.cpp b/src/waveform/renderers/waveformwidgetrenderer.cpp index 86dc06f65911..3d9e8b783ffa 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.cpp +++ b/src/waveform/renderers/waveformwidgetrenderer.cpp @@ -48,7 +48,8 @@ WaveformWidgetRenderer::WaveformWidgetRenderer(const QString& group) m_playMarkerPosition(s_defaultPlayMarkerPosition), m_passthroughEnabled(false), m_playPos(-1.0), - m_truePosSample(-1.0) { + m_truePlayPosSample(-1.0), + m_trueSlipPosSample(-1.0) { //qDebug() << "WaveformWidgetRenderer"; #ifdef WAVEFORMWIDGETRENDERER_DEBUG @@ -133,7 +134,9 @@ void WaveformWidgetRenderer::onPreRender(VSyncThread* vsyncThread) { } } - double truePlayPos = m_visualPlayPosition->getAtNextVSync(vsyncThread); + double truePlayPos = 0; + double trueSlipPos = 0; + m_visualPlayPosition->getPlaySlipAtNextVSync(vsyncThread, &truePlayPos, &trueSlipPos); // truePlayPos = -1 happens, when a new track is in buffer but m_visualPlayPosition was not updated if (m_audioSamplePerPixel > 0 && truePlayPos != -1) { @@ -143,9 +146,11 @@ void WaveformWidgetRenderer::onPreRender(VSyncThread* vsyncThread) { // Avoid pixel jitter in play position by rounding to the nearest track // pixel. m_playPos = round(truePlayPos * m_trackPixelCount) / m_trackPixelCount; + m_slipPos = round(trueSlipPos * m_trackPixelCount) / m_trackPixelCount; m_totalVSamples = static_cast(m_trackPixelCount * m_visualSamplePerPixel); m_playPosVSample = static_cast(m_playPos * m_totalVSamples); - m_truePosSample = truePlayPos * static_cast(m_trackSamples); + m_truePlayPosSample = truePlayPos * static_cast(m_trackSamples); + m_trueSlipPosSample = trueSlipPos * static_cast(m_trackSamples); double leftOffset = m_playMarkerPosition; double rightOffset = 1.0 - m_playMarkerPosition; @@ -159,11 +164,14 @@ void WaveformWidgetRenderer::onPreRender(VSyncThread* vsyncThread) { // "displayedLengthLeft=" << displayedLengthLeft << // "displayedLengthRight=" << displayedLengthRight; + m_displayedSlipPosition = m_slipPos - displayedLengthLeft; m_firstDisplayedPosition = m_playPos - displayedLengthLeft; m_lastDisplayedPosition = m_playPos + displayedLengthRight; } else { m_playPos = -1.0; // disable renderers - m_truePosSample = -1.0; + m_slipPos = -1.0; // disable renderers + m_truePlayPosSample = -1.0; + m_trueSlipPosSample = -1.0; } // qDebug() << "WaveformWidgetRenderer::onPreRender" << diff --git a/src/waveform/renderers/waveformwidgetrenderer.h b/src/waveform/renderers/waveformwidgetrenderer.h index 5570ed8fc089..6130ecaf5db7 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.h +++ b/src/waveform/renderers/waveformwidgetrenderer.h @@ -52,6 +52,10 @@ class WaveformWidgetRenderer { CuePointer getCuePointerFromIndex(int cueIndex) const; + double getDisplayedSlipPosition() const { + return m_displayedSlipPosition; + } + double getFirstDisplayedPosition() const { return m_firstDisplayedPosition; } @@ -75,7 +79,7 @@ class WaveformWidgetRenderer { // stable and deterministic // Transform sample index to pixel in track. inline double transformSamplePositionInRendererWorld(double samplePosition) const { - if (std::abs(samplePosition - m_truePosSample) < 1.f) { + if (std::abs(samplePosition - m_truePlayPosSample) < 1.f) { // When asked for the sample position that corresponds with the play // marker, return the play market pixel position. This avoids a rare // rounding issue where a marker at that sample position would be @@ -86,6 +90,21 @@ class WaveformWidgetRenderer { 2 / m_audioSamplePerPixel; } + // this "regulate" against visual sampling to make the position in widget + // stable and deterministic + // Transform sample index to pixel in track. + inline double transformSampleSlipPositionInRendererWorld(double samplePosition) const { + if (std::abs(samplePosition - m_trueSlipPosSample) < 1.f) { + // When asked for the sample position that corresponds with the play + // marker, return the play market pixel position. This avoids a rare + // rounding issue where a marker at that sample position would be + // 1 pixel off. + return m_playMarkerPosition * getLength(); + } + return (samplePosition - m_displayedSlipPosition * m_trackSamples) / + 2 / m_audioSamplePerPixel; + } + int getPlayPosVSample() const { return m_playPosVSample; } @@ -187,6 +206,7 @@ class WaveformWidgetRenderer { WaveformSignalColors m_colors; QColor m_passthroughLabelColor; + double m_displayedSlipPosition; double m_firstDisplayedPosition; double m_lastDisplayedPosition; double m_trackPixelCount; @@ -234,5 +254,7 @@ class WaveformWidgetRenderer { bool m_passthroughEnabled; double m_playPos; - double m_truePosSample; + double m_slipPos; + double m_truePlayPosSample; + double m_trueSlipPosSample; }; diff --git a/src/waveform/widgets/allshader/rgbwaveformwidget.cpp b/src/waveform/widgets/allshader/rgbwaveformwidget.cpp index 605dbe376293..3862e1325687 100644 --- a/src/waveform/widgets/allshader/rgbwaveformwidget.cpp +++ b/src/waveform/widgets/allshader/rgbwaveformwidget.cpp @@ -5,6 +5,7 @@ #include "waveform/renderers/allshader/waveformrendererendoftrack.h" #include "waveform/renderers/allshader/waveformrendererpreroll.h" #include "waveform/renderers/allshader/waveformrendererrgb.h" +#include "waveform/renderers/allshader/waveformrendererslipmode.h" #include "waveform/renderers/allshader/waveformrendermark.h" #include "waveform/renderers/allshader/waveformrendermarkrange.h" #include "waveform/widgets/allshader/moc_rgbwaveformwidget.cpp" @@ -20,6 +21,7 @@ RGBWaveformWidget::RGBWaveformWidget(const QString& group, QWidget* parent) addRenderer(); addRenderer(); addRenderer(); + addRenderer(); m_initSuccess = init(); }