From 3e95b90a09e430689b502a6d6d1d7b157e25b748 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 23 Mar 2024 16:30:56 +0100 Subject: [PATCH 01/30] show beats until next marker, using digitsrenderer with digits texture (generated) and fade out during beat duration --- CMakeLists.txt | 2 + src/shaders/coloredtextureshader.cpp | 35 ++++ src/shaders/coloredtextureshader.h | 39 ++++ .../renderers/allshader/digitsrenderer.cpp | 175 ++++++++++++++++++ .../renderers/allshader/digitsrenderer.h | 35 ++++ .../allshader/waveformrendermark.cpp | 86 ++++++++- .../renderers/allshader/waveformrendermark.h | 11 +- .../renderers/waveformwidgetrenderer.h | 4 + 8 files changed, 377 insertions(+), 10 deletions(-) create mode 100644 src/shaders/coloredtextureshader.cpp create mode 100644 src/shaders/coloredtextureshader.h create mode 100644 src/waveform/renderers/allshader/digitsrenderer.cpp create mode 100644 src/waveform/renderers/allshader/digitsrenderer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef05d6d48eb..75242eb7f86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1505,12 +1505,14 @@ if(QOPENGL) src/shaders/slipmodeshader.cpp src/shaders/patternshader.cpp src/shaders/rgbashader.cpp + src/shaders/coloredtextureshader.cpp src/shaders/rgbshader.cpp src/shaders/shader.cpp src/shaders/textureshader.cpp src/shaders/unicolorshader.cpp src/shaders/vinylqualityshader.cpp src/util/opengltexture2d.cpp + src/waveform/renderers/allshader/digitsrenderer.cpp src/waveform/renderers/allshader/matrixforwidgetgeometry.cpp src/waveform/renderers/allshader/waveformrenderbackground.cpp src/waveform/renderers/allshader/waveformrenderbeat.cpp diff --git a/src/shaders/coloredtextureshader.cpp b/src/shaders/coloredtextureshader.cpp new file mode 100644 index 00000000000..4727fff27e7 --- /dev/null +++ b/src/shaders/coloredtextureshader.cpp @@ -0,0 +1,35 @@ +#include "shaders/coloredtextureshader.h" + +using namespace mixxx; + +void ColoredTextureShader::init() { + QString vertexShaderCode = QStringLiteral(R"--( +uniform highp mat4 matrix; +attribute highp vec4 position; // use vec4 here (will be padded) for matrix multiplication +attribute highp vec2 texcoord; +varying highp vec2 vTexcoord; +void main() +{ + vTexcoord = texcoord; + gl_Position = matrix * position; +} +)--"); + + QString fragmentShaderCode = QStringLiteral(R"--( +uniform sampler2D texture; +uniform highp vec4 color; +varying highp vec2 vTexcoord; +void main() +{ + gl_FragColor = texture2D(texture, vTexcoord) * color; +} +)--"); + + load(vertexShaderCode, fragmentShaderCode); + + m_matrixLocation = uniformLocation("matrix"); + m_positionLocation = attributeLocation("position"); + m_texcoordLocation = attributeLocation("texcoord"); + m_textureLocation = uniformLocation("texture"); + m_colorLocation = uniformLocation("color"); +} diff --git a/src/shaders/coloredtextureshader.h b/src/shaders/coloredtextureshader.h new file mode 100644 index 00000000000..a25df8106d0 --- /dev/null +++ b/src/shaders/coloredtextureshader.h @@ -0,0 +1,39 @@ +#pragma once + +#include "shaders/shader.h" + +namespace mixxx { +class ColoredTextureShader; +} + +class mixxx::ColoredTextureShader final : public mixxx::Shader { + public: + ColoredTextureShader() = default; + ~ColoredTextureShader() = default; + void init(); + + int matrixLocation() const { + return m_matrixLocation; + } + int positionLocation() const { + return m_positionLocation; + } + int texcoordLocation() const { + return m_texcoordLocation; + } + int textureLocation() const { + return m_textureLocation; + } + int colorLocation() const { + return m_colorLocation; + } + + private: + int m_matrixLocation; + int m_positionLocation; + int m_texcoordLocation; + int m_textureLocation; + int m_colorLocation; + + DISALLOW_COPY_AND_ASSIGN(ColoredTextureShader) +}; diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp new file mode 100644 index 00000000000..77f241b7bd0 --- /dev/null +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -0,0 +1,175 @@ +#include "waveform/renderers/allshader/digitsrenderer.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/texture.h" +#include "waveform/renderers/allshader/matrixforwidgetgeometry.h" +#include "waveform/renderers/allshader/vertexdata.h" + +// Render digits using a texture (generated) with digits with blurred dark outline + +allshader::DigitsRenderer::~DigitsRenderer() = default; + +void allshader::DigitsRenderer::init() { + initializeOpenGLFunctions(); + m_shader.init(); +} + +float allshader::DigitsRenderer::height() const { + return m_pTexture->height(); +} + +void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { + QFont font; + + font.setFamily("Open Sans"); + font.setPixelSize(18); + + QFontMetricsF metrics{font}; + + qreal totalTextWidth = 0; + qreal maxTextHeight = 0; + for (int i = 0; i < 10; i++) { + const auto text = QString::number(i); + const auto rect = metrics.tightBoundingRect(text); + maxTextHeight = std::max(maxTextHeight, rect.height()); + totalTextWidth += metrics.horizontalAdvance(text) + 6; + } + + totalTextWidth = std::ceil(totalTextWidth); + + QImage image(static_cast(totalTextWidth * devicePixelRatio), + static_cast(std::ceil(maxTextHeight + 6) * devicePixelRatio), + QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(devicePixelRatio); + image.fill(Qt::transparent); + + // Draw digits with dark outline + QPainter painter; + QPen pen(QColor(0, 0, 0, 192)); + pen.setWidth(3); + + painter.begin(&image); + painter.setRenderHint(QPainter::Antialiasing); + painter.setWorldMatrixEnabled(false); + painter.setBrush(QColor(0, 0, 0, 224)); + painter.setPen(pen); + painter.setFont(font); + qreal x = 0; + for (int i = 0; i < 10; i++) { + const auto text = QString::number(i); + QPainterPath path; + path.addText(QPointF(x + 3, maxTextHeight + 3), font, text); + painter.drawPath(path); + x += metrics.horizontalAdvance(text) + 6; + } + painter.end(); + + // Apply Gaussian blur to dark outline + + QGraphicsBlurEffect* blur = new QGraphicsBlurEffect; // ownership passed to item + blur->setBlurRadius(3); + + QGraphicsScene scene; + QGraphicsPixmapItem item; + item.setPixmap(QPixmap::fromImage(image)); + item.setGraphicsEffect(blur); + scene.addItem(&item); + + image.fill(Qt::transparent); + painter.begin(&image); + scene.render(&painter, QRectF(), QRectF(0, 0, image.width(), image.height())); + + // Draw digits foreground + painter.setPen(Qt::white); + painter.setFont(font); + + x = 0; + for (int i = 0; i < 10; i++) { + const auto text = QString::number(i); + painter.drawText(QPointF(x + 3, maxTextHeight + 3), text); + m_offset[i] = x / totalTextWidth; + m_width[i] = metrics.horizontalAdvance(text) + 6; + x += metrics.horizontalAdvance(text) + 6; + } + + painter.end(); + + m_pTexture = createTexture(image); +} + +void allshader::DigitsRenderer::drawNumber(const QMatrix4x4& matrix, + float x, + float y, + int number, + QColor color, + float devicePixelRatio) { + if (number < 0) { + assert(false); + return; + } + + int digits[10]; + int ndigits = 0; + + do { + digits[ndigits++] = number % 10; + number /= 10; + } while (number && ndigits < 10); + + VertexData posVertices; + VertexData texVertices; + + posVertices.reserve(ndigits * 6); // two triangles per digit + texVertices.reserve(ndigits * 6); + + while (ndigits) { + int digit = digits[--ndigits]; + + texVertices.addRectangle(m_offset[digit], 0.f, digit == 9 ? 1.0 : m_offset[digit + 1], 1.f); + posVertices.addRectangle(x, + y, + x + m_width[digit], + y + + static_cast( + m_pTexture->height() / devicePixelRatio)); + x += m_width[digit] - 5.0; + } + + m_shader.bind(); + + const int matrixLocation = m_shader.uniformLocation("matrix"); + const int textureLocation = m_shader.uniformLocation("texture"); + const int positionLocation = m_shader.attributeLocation("position"); + const int texcoordLocation = m_shader.attributeLocation("texcoord"); + const int colorLocation = m_shader.colorLocation(); + + m_shader.setUniformValue(matrixLocation, matrix); + m_shader.setUniformValue(colorLocation, color); + + m_shader.enableAttributeArray(positionLocation); + m_shader.setAttributeArray( + positionLocation, GL_FLOAT, posVertices.constData(), 2); + m_shader.enableAttributeArray(texcoordLocation); + m_shader.setAttributeArray( + texcoordLocation, GL_FLOAT, texVertices.constData(), 2); + + m_shader.setUniformValue(textureLocation, 0); + + m_pTexture->bind(); + + glDrawArrays(GL_TRIANGLES, 0, posVertices.size()); + + m_pTexture->release(); + + m_shader.disableAttributeArray(positionLocation); + m_shader.disableAttributeArray(texcoordLocation); + m_shader.release(); +} diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h new file mode 100644 index 00000000000..a638c46759a --- /dev/null +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "shaders/coloredtextureshader.h" + +class QOpenGLTexture; + +namespace allshader { +class DigitsRenderer; +} + +class allshader::DigitsRenderer : public QOpenGLFunctions { + public: + DigitsRenderer() = default; + ~DigitsRenderer(); + + void init(); + void generateTexture(float devicePixelRatio); + void drawNumber(const QMatrix4x4& matrix, + float x, + float y, + int number, + QColor color, + float devicePixelRatio); + float height() const; + + private: + mixxx::ColoredTextureShader m_shader; + std::unique_ptr m_pTexture; + qreal m_offset[10]; + qreal m_width[10]; + + DISALLOW_COPY_AND_ASSIGN(DigitsRenderer); +}; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index eaff262465e..94f34b55d21 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -3,6 +3,7 @@ #include #include +#include "track/track.h" #include "util/colorcomponents.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/allshader/rgbadata.h" @@ -47,20 +48,24 @@ allshader::WaveformRenderMark::WaveformRenderMark( WaveformWidgetRenderer* waveformWidget, ::WaveformRendererAbstract::PositionSource type) : WaveformRenderMarkBase(waveformWidget, false), + m_beatDistance(0), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { } void allshader::WaveformRenderMark::initializeGL() { allshader::WaveformRendererAbstract::initializeGL(); + m_digitsRenderer.init(); m_rgbaShader.init(); m_textureShader.init(); // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); + m_digitsRenderer.generateTexture(m_waveformRenderer->getDevicePixelRatio()); } -void allshader::WaveformRenderMark::drawTexture(float x, float y, QOpenGLTexture* texture) { +void allshader::WaveformRenderMark::drawTexture( + const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture) { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const float texx1 = 0.f; const float texy1 = 0.f; @@ -75,8 +80,6 @@ void allshader::WaveformRenderMark::drawTexture(float x, float y, QOpenGLTexture const float posarray[] = {posx1, posy1, posx2, posy1, posx1, posy2, posx2, posy2}; const float texarray[] = {texx1, texy1, texx2, texy1, texx1, texy2, texx2, texy2}; - const QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - m_textureShader.bind(); const int matrixLocation = m_textureShader.uniformLocation("matrix"); @@ -106,7 +109,8 @@ void allshader::WaveformRenderMark::drawTexture(float x, float y, QOpenGLTexture m_textureShader.release(); } -void allshader::WaveformRenderMark::drawMark(const QRectF& rect, QColor color) { +void allshader::WaveformRenderMark::drawMark( + const QMatrix4x4& matrix, const QRectF& rect, QColor color) { // draw a gradient towards transparency at the upper and lower 25% of the waveform view const float qh = static_cast(std::floor(rect.height() * 0.25)); @@ -131,8 +135,6 @@ void allshader::WaveformRenderMark::drawMark(const QRectF& rect, QColor color) { rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); rgbaData.addForRectangleGradient(r, g, b, a, r, g, b, 0.f); - QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); - m_rgbaShader.bind(); const int matrixLocation = m_rgbaShader.matrixLocation(); @@ -177,12 +179,18 @@ void allshader::WaveformRenderMark::paintGL() { } updateMarkImages(); + QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); + + const double playPosition = m_waveformRenderer->getTruePosSample(); + double nextMarkPosition = std::numeric_limits::max(); + for (const auto& pMark : std::as_const(m_marks)) { QOpenGLTexture* pTexture = static_cast(pMark->m_pGraphics.get()) ->texture(); const double samplePosition = pMark->getSamplePosition(); + if (samplePosition != Cue::kNoPosition) { const float currentMarkPoint = std::round( @@ -192,6 +200,9 @@ void allshader::WaveformRenderMark::paintGL() { samplePosition, positionType)) * devicePixelRatio) / devicePixelRatio; + if (samplePosition >= playPosition + 1.0 && samplePosition < nextMarkPosition) { + nextMarkPosition = samplePosition; + } const double sampleEndPosition = pMark->getSampleEndPosition(); // Pixmaps are expected to have the mark stroke at the center, @@ -226,7 +237,7 @@ void allshader::WaveformRenderMark::paintGL() { QColor color = pMark->fillColor(); color.setAlphaF(0.4f); - drawMark( + drawMark(matrix, QRectF(QPointF(currentMarkPoint, 0), QPointF(currentMarkEndPoint, m_waveformRenderer @@ -256,7 +267,28 @@ void allshader::WaveformRenderMark::paintGL() { const float markHalfWidth = m_playPosMarkTexture.width() / devicePixelRatio / 2.f; const float drawOffset = currentMarkPoint - markHalfWidth; - drawTexture(drawOffset, 0.f, &m_playPosMarkTexture); + drawTexture(matrix, drawOffset, 0.f, m_pPlayPosMarkTexture.get()); + } + + updateBeatDistance(playPosition, nextMarkPosition); + + if (m_beatDistance != 0) { + // Show the number of beats until the next marker, + // fading out over the duration of the beat. + double alpha = (playPosition - m_currentBeatPosition) / + (m_nextBeatPosition - m_currentBeatPosition); + alpha = std::max(0., std::min(1., alpha)); + // Cubic curve + alpha = 1.0 - alpha * alpha * alpha; + int ialpha = static_cast(alpha * 255.0); + m_digitsRenderer.drawNumber(matrix, + currentMarkPoint + 8.f, + m_waveformRenderer->getBreadth() / 2 - + m_digitsRenderer.height() / 2 / devicePixelRatio, + m_beatDistance, + QColor(255, 255, 255, ialpha), + devicePixelRatio); +>>>>>>> ea0a4d064d (show beats until next marker, using digitsrenderer with digits texture (generated) and fade out during beat duration) } } @@ -351,3 +383,41 @@ void allshader::WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { pMark->m_pGraphics = std::make_unique( pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); } + +void allshader::WaveformRenderMark::updateBeatDistance( + double playPosition, double nextMarkPosition) { + if (nextMarkPosition == std::numeric_limits::max()) { + m_beatDistance = 0; + return; + } + + TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); + + if (!trackInfo) { + m_beatDistance = 0; + return; + } + + mixxx::BeatsPointer trackBeats = trackInfo->getBeats(); + if (!trackBeats) { + m_beatDistance = 0; + return; + } + + auto itA = trackBeats->iteratorFrom( + mixxx::audio::FramePos::fromEngineSamplePos(playPosition)); + auto itB = trackBeats->iteratorFrom( + mixxx::audio::FramePos::fromEngineSamplePos(nextMarkPosition)); + + if (std::abs(itA->toEngineSamplePos() - playPosition) < 1) { + m_currentBeatPosition = itA->toEngineSamplePos(); + m_beatDistance = std::distance(itA, itB); + itA++; + m_nextBeatPosition = itA->toEngineSamplePos(); + } else { + m_nextBeatPosition = itA->toEngineSamplePos(); + itA--; + m_currentBeatPosition = itA->toEngineSamplePos(); + m_beatDistance = std::distance(itA, itB); + } +} diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 3c4404e957e..17b5dc86850 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -5,6 +5,7 @@ #include "shaders/rgbashader.h" #include "shaders/textureshader.h" #include "util/opengltexture2d.h" +#include "waveform/renderers/allshader/digitsrenderer.h" #include "waveform/renderers/allshader/waveformrendererabstract.h" #include "waveform/renderers/waveformrendermarkbase.h" @@ -47,12 +48,18 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, QPointF p2, QPointF p3); - void drawMark(const QRectF& rect, QColor color); - void drawTexture(float x, float y, QOpenGLTexture* texture); + void drawMark(const QMatrix4x4& matrix, const QRectF& rect, QColor color); + void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); + + void updateBeatDistance(double playPosition, double markerPosition); mixxx::RGBAShader m_rgbaShader; mixxx::TextureShader m_textureShader; OpenGLTexture2D m_playPosMarkTexture; + DigitsRenderer m_digitsRenderer; + int m_beatDistance; + double m_currentBeatPosition; + double m_nextBeatPosition; bool m_isSlipRenderer; diff --git a/src/waveform/renderers/waveformwidgetrenderer.h b/src/waveform/renderers/waveformwidgetrenderer.h index a2b82dd5e53..5d724da7e9d 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.h +++ b/src/waveform/renderers/waveformwidgetrenderer.h @@ -68,6 +68,10 @@ class WaveformWidgetRenderer { return m_lastDisplayedPosition[type]; } + double getTruePosSample() const { + return m_truePosSample; + } + void setZoom(double zoom); void setDisplayBeatGrid(bool set); From d7ea74fd3432bc51ffeca4a133d125c950a2966b Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 25 Mar 2024 20:46:59 +0100 Subject: [PATCH 02/30] also show time until marker --- .../renderers/allshader/digitsrenderer.cpp | 69 +++++++++--------- .../renderers/allshader/digitsrenderer.h | 8 +-- .../allshader/waveformrendermark.cpp | 72 +++++++++++++++---- .../renderers/allshader/waveformrendermark.h | 4 ++ 4 files changed, 103 insertions(+), 50 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 77f241b7bd0..6ae178ef21b 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -15,6 +15,18 @@ // Render digits using a texture (generated) with digits with blurred dark outline +namespace { +int charToIndex(char ch) { + if (ch >= '0' && ch <= '9') { + return static_cast(ch - '0'); + } + if (ch == ':') { + return 10; + } + return 11; +} +} // namespace + allshader::DigitsRenderer::~DigitsRenderer() = default; void allshader::DigitsRenderer::init() { @@ -23,12 +35,12 @@ void allshader::DigitsRenderer::init() { } float allshader::DigitsRenderer::height() const { - return m_pTexture->height(); + return static_cast(m_pTexture->height()); } void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { QFont font; - + const char* str = "0123456789:."; font.setFamily("Open Sans"); font.setPixelSize(18); @@ -36,8 +48,9 @@ void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { qreal totalTextWidth = 0; qreal maxTextHeight = 0; - for (int i = 0; i < 10; i++) { - const auto text = QString::number(i); + for (int i = 0; i < 12; i++) { + assert(charToIndex(str[i]) == i); + const QString text(str[i]); const auto rect = metrics.tightBoundingRect(text); maxTextHeight = std::max(maxTextHeight, rect.height()); totalTextWidth += metrics.horizontalAdvance(text) + 6; @@ -63,8 +76,8 @@ void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { painter.setPen(pen); painter.setFont(font); qreal x = 0; - for (int i = 0; i < 10; i++) { - const auto text = QString::number(i); + for (int i = 0; i < 12; i++) { + const QString text(str[i]); QPainterPath path; path.addText(QPointF(x + 3, maxTextHeight + 3), font, text); painter.drawPath(path); @@ -92,55 +105,43 @@ void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { painter.setFont(font); x = 0; - for (int i = 0; i < 10; i++) { - const auto text = QString::number(i); + for (int i = 0; i < 12; i++) { + const QString text(str[i]); painter.drawText(QPointF(x + 3, maxTextHeight + 3), text); - m_offset[i] = x / totalTextWidth; - m_width[i] = metrics.horizontalAdvance(text) + 6; + m_offset[i] = static_cast(x / totalTextWidth); + m_width[i] = static_cast(metrics.horizontalAdvance(text) + 6); x += metrics.horizontalAdvance(text) + 6; } + m_offset[12] = 1.f; painter.end(); m_pTexture = createTexture(image); } -void allshader::DigitsRenderer::drawNumber(const QMatrix4x4& matrix, +void allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, float x, float y, - int number, + const QString& s, QColor color, float devicePixelRatio) { - if (number < 0) { - assert(false); - return; - } - - int digits[10]; - int ndigits = 0; - - do { - digits[ndigits++] = number % 10; - number /= 10; - } while (number && ndigits < 10); + const int n = s.length(); VertexData posVertices; VertexData texVertices; - posVertices.reserve(ndigits * 6); // two triangles per digit - texVertices.reserve(ndigits * 6); + posVertices.reserve(n * 6); // two triangles per character + texVertices.reserve(n * 6); - while (ndigits) { - int digit = digits[--ndigits]; + for (int i = 0; i < n; i++) { + int index = charToIndex(s[i].cell()); - texVertices.addRectangle(m_offset[digit], 0.f, digit == 9 ? 1.0 : m_offset[digit + 1], 1.f); + texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); posVertices.addRectangle(x, y, - x + m_width[digit], - y + - static_cast( - m_pTexture->height() / devicePixelRatio)); - x += m_width[digit] - 5.0; + x + m_width[index], + y + height() / devicePixelRatio); + x += m_width[index] - 5.0; } m_shader.bind(); diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index a638c46759a..75c7ec23be1 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -17,10 +17,10 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { void init(); void generateTexture(float devicePixelRatio); - void drawNumber(const QMatrix4x4& matrix, + void draw(const QMatrix4x4& matrix, float x, float y, - int number, + const QString& s, QColor color, float devicePixelRatio); float height() const; @@ -28,8 +28,8 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { private: mixxx::ColoredTextureShader m_shader; std::unique_ptr m_pTexture; - qreal m_offset[10]; - qreal m_width[10]; + float m_offset[13]; + float m_width[12]; DISALLOW_COPY_AND_ASSIGN(DigitsRenderer); }; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 94f34b55d21..5139b7046a0 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -44,14 +44,36 @@ class TextureGraphics : public WaveformMark::Graphics { // The boolean argument for the WaveformRenderMarkBase constructor indicates // that updateMarkImages should not be called immediately. +namespace { +QString timeToString(double t) { + int hundreds = std::lround(t * 100.0); + int seconds = hundreds / 100; + hundreds -= seconds * 100; + int minutes = seconds / 60; + seconds -= minutes * 60; + + return QString::number(minutes) + (seconds < 10 ? ":0" : ":") + + QString::number(seconds) + (hundreds < 10 ? ".0" : ".") + + QString::number(hundreds); +} + +} // namespace + allshader::WaveformRenderMark::WaveformRenderMark( - WaveformWidgetRenderer* waveformWidget, - ::WaveformRendererAbstract::PositionSource type) - : WaveformRenderMarkBase(waveformWidget, false), - m_beatDistance(0), + WaveformWidgetRenderer* waveformWidget) + : ::WaveformRenderMarkBase(waveformWidget, false), + m_beatsUntilMark(0), + m_timeUntilMark(0.0), + m_pTimeRemainingControl(nullptr), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip) { } +bool allshader::WaveformRenderMark::init() { + m_pTimeRemainingControl.reset(new ControlProxy( + m_waveformRenderer->getGroup(), "time_remaining")); + return true; +} + void allshader::WaveformRenderMark::initializeGL() { allshader::WaveformRendererAbstract::initializeGL(); m_digitsRenderer.init(); @@ -270,11 +292,12 @@ void allshader::WaveformRenderMark::paintGL() { drawTexture(matrix, drawOffset, 0.f, m_pPlayPosMarkTexture.get()); } - updateBeatDistance(playPosition, nextMarkPosition); + updateUntilMark(playPosition, nextMarkPosition); - if (m_beatDistance != 0) { + if (m_timeUntilMark != 0.0) { // Show the number of beats until the next marker, // fading out over the duration of the beat. + /* double alpha = (playPosition - m_currentBeatPosition) / (m_nextBeatPosition - m_currentBeatPosition); alpha = std::max(0., std::min(1., alpha)); @@ -286,6 +309,24 @@ void allshader::WaveformRenderMark::paintGL() { m_waveformRenderer->getBreadth() / 2 - m_digitsRenderer.height() / 2 / devicePixelRatio, m_beatDistance, + */ + const int ialpha = 255; + + const float yoffset = 10.f; + + m_digitsRenderer.draw(matrix, + drawOffset + 8.f, + m_waveformRenderer->getBreadth() / 2.f - yoffset - + m_digitsRenderer.height() / 2.f / devicePixelRatio, + QString::number(m_beatsUntilMark), + QColor(255, 255, 255, ialpha), + devicePixelRatio); + m_digitsRenderer.draw(matrix, + drawOffset + 8.f, + m_waveformRenderer->getBreadth() / 2.f + yoffset - + m_digitsRenderer.height() / 2.f / devicePixelRatio, + timeToString(m_timeUntilMark), +>>>>>>> 13349d0e47 (also show time until marker) QColor(255, 255, 255, ialpha), devicePixelRatio); >>>>>>> ea0a4d064d (show beats until next marker, using digitsrenderer with digits texture (generated) and fade out during beat duration) @@ -384,23 +425,25 @@ void allshader::WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); } -void allshader::WaveformRenderMark::updateBeatDistance( +void allshader::WaveformRenderMark::updateUntilMark( double playPosition, double nextMarkPosition) { + m_beatsUntilMark = 0; + m_timeUntilMark = 0.0; if (nextMarkPosition == std::numeric_limits::max()) { - m_beatDistance = 0; return; } TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); if (!trackInfo) { - m_beatDistance = 0; return; } + const double endPosition = m_waveformRenderer->getTrackSamples(); + const double remainingTime = m_pTimeRemainingControl->get(); + mixxx::BeatsPointer trackBeats = trackInfo->getBeats(); if (!trackBeats) { - m_beatDistance = 0; return; } @@ -411,13 +454,18 @@ void allshader::WaveformRenderMark::updateBeatDistance( if (std::abs(itA->toEngineSamplePos() - playPosition) < 1) { m_currentBeatPosition = itA->toEngineSamplePos(); - m_beatDistance = std::distance(itA, itB); + m_beatsUntilMark = std::distance(itA, itB); itA++; m_nextBeatPosition = itA->toEngineSamplePos(); } else { m_nextBeatPosition = itA->toEngineSamplePos(); itA--; m_currentBeatPosition = itA->toEngineSamplePos(); - m_beatDistance = std::distance(itA, itB); + m_beatsUntilMark = std::distance(itA, itB); } + // As endPosition - playPosition corresponds with remainingTime, + // we calculate the proportional part of nextMarkPosition - playPosition + m_timeUntilMark = std::max(0.0, + remainingTime * (nextMarkPosition - playPosition) / + (endPosition - playPosition)); } diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 17b5dc86850..42b5e343e0e 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -33,6 +33,8 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, return this; } + bool init() override; + void initializeGL() override; void paintGL() override; void resizeGL(int w, int h) override; @@ -52,6 +54,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); void updateBeatDistance(double playPosition, double markerPosition); + void updateUntilMark(double playPosition, double markerPosition); mixxx::RGBAShader m_rgbaShader; mixxx::TextureShader m_textureShader; @@ -60,6 +63,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, int m_beatDistance; double m_currentBeatPosition; double m_nextBeatPosition; + std::unique_ptr m_pTimeRemainingControl; bool m_isSlipRenderer; From 9daeef7feacc31b84977ab60da1a3e10731506e4 Mon Sep 17 00:00:00 2001 From: m0dB Date: Tue, 26 Mar 2024 21:13:57 +0100 Subject: [PATCH 03/30] float instead of double to fix ubuntu compilation --- src/waveform/renderers/allshader/digitsrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 6ae178ef21b..9036bf51934 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -141,7 +141,7 @@ void allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, y, x + m_width[index], y + height() / devicePixelRatio); - x += m_width[index] - 5.0; + x += m_width[index] - 5.f; } m_shader.bind(); From dcb6baa8d38e657a678658b419b4579af323bdbd Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 7 Apr 2024 16:25:28 +0200 Subject: [PATCH 04/30] until next marker prefs --- src/preferences/dialog/dlgprefwaveform.cpp | 11 +++ src/preferences/dialog/dlgprefwaveformdlg.ui | 79 +++++++++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 0d82c0a592b..cde12472f96 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -41,6 +41,17 @@ DlgPrefWaveform::DlgPrefWaveform( defaultZoomComboBox->addItem(QString::number(100 / static_cast(i), 'f', 1) + " %"); } + // Populate untilNextMarker options + untilNextMarkerShowComboBox->addItem(tr("None")); + untilNextMarkerShowComboBox->addItem(tr("Beats")); + untilNextMarkerShowComboBox->addItem(tr("Time")); + untilNextMarkerShowComboBox->addItem(tr("Beats + Time")); + untilNextMarkerShowComboBox->addItem(tr("Beats + Time (multi-line)")); + + untilNextMarkerPlacementComboBox->addItem(tr("Top")); + untilNextMarkerPlacementComboBox->addItem(tr("Center")); + untilNextMarkerPlacementComboBox->addItem(tr("Bottom")); + // The GUI is not fully setup so connecting signals before calling // slotUpdate can generate rebootMixxxView calls. // TODO(XXX): Improve this awkwardness. diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 4feeb704a90..6e099d1ed2e 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -459,6 +459,81 @@ Select from different types of displays for the waveform, which differ primarily + + + Until next marker + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Show + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilNextMarkerShowComboBox + + + + + + + + + + Placement + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilNextMarkerPlacementComboBox + + + + + + + + + + Size + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilNextMarkerSizeSpinBox + + + + + + + + + + pt + + + 10 + + + 20 + + + 15 + + + + + Caching @@ -468,7 +543,7 @@ Select from different types of displays for the waveform, which differ primarily - + @@ -520,7 +595,7 @@ Select from different types of displays for the waveform, which differ primarily - + OpenGL status From 7f0f60a6be1452fd6240f1d65d0217cbdcef9f54 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 7 Apr 2024 18:46:55 +0200 Subject: [PATCH 05/30] settings for until next marker display --- src/preferences/dialog/dlgprefwaveform.cpp | 39 +++++++- src/preferences/dialog/dlgprefwaveform.h | 4 +- src/preferences/dialog/dlgprefwaveformdlg.ui | 14 +-- .../renderers/allshader/digitsrenderer.cpp | 9 +- .../renderers/allshader/digitsrenderer.h | 4 +- .../allshader/waveformrendermark.cpp | 94 +++++++++++++------ .../renderers/allshader/waveformrendermark.h | 2 + src/waveform/waveformwidgetfactory.cpp | 63 +++++++++++++ src/waveform/waveformwidgetfactory.h | 29 ++++++ 9 files changed, 213 insertions(+), 45 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index cde12472f96..89eead0805b 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -48,9 +48,9 @@ DlgPrefWaveform::DlgPrefWaveform( untilNextMarkerShowComboBox->addItem(tr("Beats + Time")); untilNextMarkerShowComboBox->addItem(tr("Beats + Time (multi-line)")); - untilNextMarkerPlacementComboBox->addItem(tr("Top")); - untilNextMarkerPlacementComboBox->addItem(tr("Center")); - untilNextMarkerPlacementComboBox->addItem(tr("Bottom")); + untilNextMarkerAlignComboBox->addItem(tr("Top")); + untilNextMarkerAlignComboBox->addItem(tr("Center")); + untilNextMarkerAlignComboBox->addItem(tr("Bottom")); // The GUI is not fully setup so connecting signals before calling // slotUpdate can generate rebootMixxxView calls. @@ -142,6 +142,18 @@ DlgPrefWaveform::DlgPrefWaveform( &QSlider::valueChanged, this, &DlgPrefWaveform::slotSetPlayMarkerPosition); + connect(untilNextMarkerShowComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &DlgPrefWaveform::slotSetUntilNextMarkerShow); + connect(untilNextMarkerAlignComboBox, + QOverload::of(&QComboBox::currentIndexChanged), + this, + &DlgPrefWaveform::slotSetUntilNextMarkerAlign); + connect(untilNextMarkerSizeSpinBox, + QOverload::of(&QSpinBox::valueChanged), + this, + &DlgPrefWaveform::slotSetUntilNextMarkerSize); setScrollSafeGuardForAllInputWidgets(this); } @@ -180,6 +192,13 @@ void DlgPrefWaveform::slotUpdate() { beatGridAlphaSpinBox->setValue(factory->getBeatGridAlpha()); beatGridAlphaSlider->setValue(factory->getBeatGridAlpha()); + untilNextMarkerShowComboBox->setCurrentIndex( + static_cast(factory->getUntilNextMarkerShow())); + untilNextMarkerAlignComboBox->setCurrentIndex( + WaveformWidgetFactory::toUntilNextMarkerAlignIndex( + factory->getUntilNextMarkerAlign())); + untilNextMarkerSizeSpinBox->setValue(factory->getUntilNextMarkerSize()); + // By default we set RGB woverview = "2" int overviewType = m_pConfig->getValue( ConfigKey("[Waveform]","WaveformOverviewType"), 2); @@ -323,6 +342,20 @@ void DlgPrefWaveform::slotSetPlayMarkerPosition(int position) { WaveformWidgetFactory::instance()->setPlayMarkerPosition(position / 100.0); } +void DlgPrefWaveform::slotSetUntilNextMarkerShow(int index) { + WaveformWidgetFactory::instance()->setUntilNextMarkerShow( + static_cast(index)); +} + +void DlgPrefWaveform::slotSetUntilNextMarkerAlign(int index) { + WaveformWidgetFactory::instance()->setUntilNextMarkerAlign( + WaveformWidgetFactory::toUntilNextMarkerAlign(index)); +} + +void DlgPrefWaveform::slotSetUntilNextMarkerSize(int value) { + WaveformWidgetFactory::instance()->setUntilNextMarkerSize(value); +} + void DlgPrefWaveform::calculateCachedWaveformDiskUsage() { AnalysisDao analysisDao(m_pConfig); QSqlDatabase dbConnection = mixxx::DbConnectionPooled(m_pLibrary->dbConnectionPool()); diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index 4f21b490b57..03f70aba2a4 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -38,7 +38,9 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotClearCachedWaveforms(); void slotSetBeatGridAlpha(int alpha); void slotSetPlayMarkerPosition(int position); - + void slotSetUntilNextMarkerShow(int index); + void slotSetUntilNextMarkerAlign(int index); + void slotSetUntilNextMarkerSize(int value); signals: void reloadUserInterface(); diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 6e099d1ed2e..3a13ebe75b7 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -485,7 +485,7 @@ Select from different types of displays for the waveform, which differ primarily - + Placement @@ -493,12 +493,12 @@ Select from different types of displays for the waveform, which differ primarily Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - untilNextMarkerPlacementComboBox + untilNextMarkerAlignComboBox - + @@ -519,16 +519,16 @@ Select from different types of displays for the waveform, which differ primarily - pt + px - 10 + 20 - 20 + 40 - 15 + 30 diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 9036bf51934..4eafb8b33e0 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -38,11 +38,11 @@ float allshader::DigitsRenderer::height() const { return static_cast(m_pTexture->height()); } -void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { +void allshader::DigitsRenderer::generateTexture(float fontPixelSize, float devicePixelRatio) { QFont font; const char* str = "0123456789:."; font.setFamily("Open Sans"); - font.setPixelSize(18); + font.setPixelSize(fontPixelSize); QFontMetricsF metrics{font}; @@ -119,13 +119,14 @@ void allshader::DigitsRenderer::generateTexture(float devicePixelRatio) { m_pTexture = createTexture(image); } -void allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, +float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, float x, float y, const QString& s, QColor color, float devicePixelRatio) { const int n = s.length(); + const float x0 = x; VertexData posVertices; VertexData texVertices; @@ -173,4 +174,6 @@ void allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, m_shader.disableAttributeArray(positionLocation); m_shader.disableAttributeArray(texcoordLocation); m_shader.release(); + + return x - x0; } diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 75c7ec23be1..2acedc121d4 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -16,8 +16,8 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { ~DigitsRenderer(); void init(); - void generateTexture(float devicePixelRatio); - void draw(const QMatrix4x4& matrix, + void generateTexture(float fontPixelSize, float devicePixelRatio); + float draw(const QMatrix4x4& matrix, float x, float y, const QString& s, diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 5139b7046a0..7424fdcbc57 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -9,6 +9,7 @@ #include "waveform/renderers/allshader/rgbadata.h" #include "waveform/renderers/allshader/vertexdata.h" #include "waveform/renderers/waveformwidgetrenderer.h" +#include "waveform/waveformwidgetfactory.h" // On the use of QPainter: // @@ -83,7 +84,10 @@ void allshader::WaveformRenderMark::initializeGL() { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); - m_digitsRenderer.generateTexture(m_waveformRenderer->getDevicePixelRatio()); + m_untilNextMarkerSize = static_cast( + WaveformWidgetFactory::instance()->getUntilNextMarkerSize()); + m_digitsRenderer.generateTexture( + m_untilNextMarkerSize, m_waveformRenderer->getDevicePixelRatio()); } void allshader::WaveformRenderMark::drawTexture( @@ -292,39 +296,71 @@ void allshader::WaveformRenderMark::paintGL() { drawTexture(matrix, drawOffset, 0.f, m_pPlayPosMarkTexture.get()); } - updateUntilMark(playPosition, nextMarkPosition); - - if (m_timeUntilMark != 0.0) { - // Show the number of beats until the next marker, - // fading out over the duration of the beat. - /* - double alpha = (playPosition - m_currentBeatPosition) / - (m_nextBeatPosition - m_currentBeatPosition); - alpha = std::max(0., std::min(1., alpha)); - // Cubic curve - alpha = 1.0 - alpha * alpha * alpha; - int ialpha = static_cast(alpha * 255.0); - m_digitsRenderer.drawNumber(matrix, - currentMarkPoint + 8.f, - m_waveformRenderer->getBreadth() / 2 - - m_digitsRenderer.height() / 2 / devicePixelRatio, - m_beatDistance, - */ - const int ialpha = 255; - - const float yoffset = 10.f; + if (WaveformWidgetFactory::instance()->getUntilNextMarkerShow() != UntilNextMarkerShow::None) { + updateUntilMark(playPosition, nextMarkPosition); + drawUntilMark(matrix, drawOffset + 20); + } +} - m_digitsRenderer.draw(matrix, - drawOffset + 8.f, - m_waveformRenderer->getBreadth() / 2.f - yoffset - - m_digitsRenderer.height() / 2.f / devicePixelRatio, +void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, float x) { + const auto untilNextMarkerShow = WaveformWidgetFactory::instance()->getUntilNextMarkerShow(); + const auto untilNextMarkerAlign = WaveformWidgetFactory::instance()->getUntilNextMarkerAlign(); + const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); + + const auto untilNextMarkerSize = static_cast( + WaveformWidgetFactory::instance()->getUntilNextMarkerSize()); + if (untilNextMarkerSize != m_untilNextMarkerSize) { + m_untilNextMarkerSize = untilNextMarkerSize; + m_digitsRenderer.generateTexture(m_untilNextMarkerSize, + m_waveformRenderer->getDevicePixelRatio()); + } + + if (m_timeUntilMark == 0.0) { + return; + } + const int ialpha = 255; + + const float ch = m_digitsRenderer.height() / devicePixelRatio; + + float y = untilNextMarkerAlign == Qt::AlignTop ? 0.f + : untilNextMarkerAlign == Qt::AlignBottom + ? m_waveformRenderer->getBreadth() - ch + : m_waveformRenderer->getBreadth() / 2.f; + + if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { + if (untilNextMarkerAlign != Qt::AlignTop) { + y -= ch; + } + } else { + if (untilNextMarkerAlign != Qt::AlignTop && untilNextMarkerAlign != Qt::AlignBottom) { + // center + y -= ch / 2.f; + } + } + + if (untilNextMarkerShow == UntilNextMarkerShow::Beats || + untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime || + untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { + const float w = m_digitsRenderer.draw(matrix, + x, + y, QString::number(m_beatsUntilMark), QColor(255, 255, 255, ialpha), devicePixelRatio); + if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime) { + x += w + std::roundf(m_untilNextMarkerSize * 0.75); + } + if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { + y += ch; + } + } + + if (untilNextMarkerShow == UntilNextMarkerShow::Time || + untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime || + untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { m_digitsRenderer.draw(matrix, - drawOffset + 8.f, - m_waveformRenderer->getBreadth() / 2.f + yoffset - - m_digitsRenderer.height() / 2.f / devicePixelRatio, + x, + y, timeToString(m_timeUntilMark), >>>>>>> 13349d0e47 (also show time until marker) QColor(255, 255, 255, ialpha), diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 42b5e343e0e..e62229db8bd 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -55,6 +55,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, void updateBeatDistance(double playPosition, double markerPosition); void updateUntilMark(double playPosition, double markerPosition); + void drawUntilMark(const QMatrix4x4& matrix, float x); mixxx::RGBAShader m_rgbaShader; mixxx::TextureShader m_textureShader; @@ -63,6 +64,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, int m_beatDistance; double m_currentBeatPosition; double m_nextBeatPosition; + float m_untilNextMarkerSize; std::unique_ptr m_pTimeRemainingControl; bool m_isSlipRenderer; diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 86616b82713..50e689d5df2 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -111,6 +111,9 @@ WaveformWidgetFactory::WaveformWidgetFactory() m_defaultZoom(WaveformWidgetRenderer::s_waveformDefaultZoom), m_zoomSync(true), m_overviewNormalized(false), + m_untilNextMarkerShow(UntilNextMarkerShow::None), + m_untilNextMarkerAlign(Qt::AlignVCenter), + m_untilNextMarkerSize(24), m_openGlAvailable(false), m_openGlesAvailable(false), m_openGLShaderAvailable(false), @@ -403,6 +406,16 @@ bool WaveformWidgetFactory::setConfig(UserSettingsPointer config) { WaveformWidgetRenderer::s_defaultPlayMarkerPosition); setPlayMarkerPosition(m_playMarkerPosition); + setUntilNextMarkerShow(static_cast( + m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerShow"), + static_cast(m_untilNextMarkerShow)))); + setUntilNextMarkerAlign(toUntilNextMarkerAlign( + m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerAlign"), + toUntilNextMarkerAlignIndex(m_untilNextMarkerAlign)))); + setUntilNextMarkerSize( + m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerSize"), + m_untilNextMarkerSize)); + return true; } @@ -1248,3 +1261,53 @@ QSurfaceFormat WaveformWidgetFactory::getSurfaceFormat(UserSettingsPointer confi #endif return format; } + +void WaveformWidgetFactory::setUntilNextMarkerShow(UntilNextMarkerShow value) { + m_untilNextMarkerShow = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerShow"), + static_cast(m_untilNextMarkerShow)); + } +} +void WaveformWidgetFactory::setUntilNextMarkerAlign(Qt::Alignment align) { + m_untilNextMarkerAlign = align; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerAlign"), + toUntilNextMarkerAlignIndex(m_untilNextMarkerAlign)); + } +} +void WaveformWidgetFactory::setUntilNextMarkerSize(int value) { + m_untilNextMarkerSize = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerSize"), m_untilNextMarkerSize); + } +} + +// static +Qt::Alignment WaveformWidgetFactory::toUntilNextMarkerAlign(int index) { + switch (index) { + case 0: + return Qt::AlignTop; + case 1: + return Qt::AlignVCenter; + case 2: + return Qt::AlignBottom; + } + assert(false); + return Qt::AlignVCenter; +} +// static +int WaveformWidgetFactory::toUntilNextMarkerAlignIndex(Qt::Alignment align) { + switch (align) { + case Qt::AlignTop: + return 0; + case Qt::AlignVCenter: + return 1; + case Qt::AlignBottom: + return 2; + default: + break; + } + assert(false); + return 1; +} diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 0a297b5fdc8..d63f4a53c68 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -55,6 +55,14 @@ class WaveformWidgetHolder { //######################################## +enum class UntilNextMarkerShow { + None, + Beats, + Time, + BeatsAndTime, + BeatsAndTimeMultiline +}; + class WaveformWidgetFactory : public QObject, public Singleton { Q_OBJECT public: @@ -98,6 +106,23 @@ class WaveformWidgetFactory : public QObject, public Singleton Date: Sun, 7 Apr 2024 18:51:30 +0200 Subject: [PATCH 06/30] use int for font pixel size --- src/waveform/renderers/allshader/digitsrenderer.cpp | 2 +- src/waveform/renderers/allshader/digitsrenderer.h | 2 +- src/waveform/renderers/allshader/waveformrendermark.cpp | 6 ++---- src/waveform/renderers/allshader/waveformrendermark.h | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 4eafb8b33e0..6f3c503b448 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -38,7 +38,7 @@ float allshader::DigitsRenderer::height() const { return static_cast(m_pTexture->height()); } -void allshader::DigitsRenderer::generateTexture(float fontPixelSize, float devicePixelRatio) { +void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float devicePixelRatio) { QFont font; const char* str = "0123456789:."; font.setFamily("Open Sans"); diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 2acedc121d4..634012d29f5 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -16,7 +16,7 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { ~DigitsRenderer(); void init(); - void generateTexture(float fontPixelSize, float devicePixelRatio); + void generateTexture(int fontPixelSize, float devicePixelRatio); float draw(const QMatrix4x4& matrix, float x, float y, diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 7424fdcbc57..bced410bb74 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -84,8 +84,7 @@ void allshader::WaveformRenderMark::initializeGL() { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); - m_untilNextMarkerSize = static_cast( - WaveformWidgetFactory::instance()->getUntilNextMarkerSize()); + m_untilNextMarkerSize = WaveformWidgetFactory::instance()->getUntilNextMarkerSize(); m_digitsRenderer.generateTexture( m_untilNextMarkerSize, m_waveformRenderer->getDevicePixelRatio()); } @@ -307,8 +306,7 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa const auto untilNextMarkerAlign = WaveformWidgetFactory::instance()->getUntilNextMarkerAlign(); const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const auto untilNextMarkerSize = static_cast( - WaveformWidgetFactory::instance()->getUntilNextMarkerSize()); + const int untilNextMarkerSize = WaveformWidgetFactory::instance()->getUntilNextMarkerSize(); if (untilNextMarkerSize != m_untilNextMarkerSize) { m_untilNextMarkerSize = untilNextMarkerSize; m_digitsRenderer.generateTexture(m_untilNextMarkerSize, diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index e62229db8bd..5125b1ebadc 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -64,7 +64,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, int m_beatDistance; double m_currentBeatPosition; double m_nextBeatPosition; - float m_untilNextMarkerSize; + int m_untilNextMarkerSize; std::unique_ptr m_pTimeRemainingControl; bool m_isSlipRenderer; From 6d999c82915d610459103451c1eaeb4a3fd750f3 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 7 Apr 2024 20:24:51 +0200 Subject: [PATCH 07/30] fix compilation on ubuntu --- src/waveform/renderers/allshader/waveformrendermark.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index bced410bb74..32446326976 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -346,7 +346,7 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa QColor(255, 255, 255, ialpha), devicePixelRatio); if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime) { - x += w + std::roundf(m_untilNextMarkerSize * 0.75); + x += w + std::round(static_cast(m_untilNextMarkerSize) * 0.75f); } if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { y += ch; From 441b1902a021a0a63d8fa0bae16a754f89d4f0ee Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 19 Apr 2024 23:57:36 +0200 Subject: [PATCH 08/30] fixes post rebase --- CMakeLists.txt | 1 - src/shaders/coloredtextureshader.cpp | 35 ----------------- src/shaders/coloredtextureshader.h | 39 ------------------- .../renderers/allshader/digitsrenderer.cpp | 12 ++---- .../renderers/allshader/digitsrenderer.h | 10 ++--- .../allshader/waveformrendermark.cpp | 10 +---- .../renderers/allshader/waveformrendermark.h | 5 +-- 7 files changed, 12 insertions(+), 100 deletions(-) delete mode 100644 src/shaders/coloredtextureshader.cpp delete mode 100644 src/shaders/coloredtextureshader.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 75242eb7f86..2061926f158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1505,7 +1505,6 @@ if(QOPENGL) src/shaders/slipmodeshader.cpp src/shaders/patternshader.cpp src/shaders/rgbashader.cpp - src/shaders/coloredtextureshader.cpp src/shaders/rgbshader.cpp src/shaders/shader.cpp src/shaders/textureshader.cpp diff --git a/src/shaders/coloredtextureshader.cpp b/src/shaders/coloredtextureshader.cpp deleted file mode 100644 index 4727fff27e7..00000000000 --- a/src/shaders/coloredtextureshader.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "shaders/coloredtextureshader.h" - -using namespace mixxx; - -void ColoredTextureShader::init() { - QString vertexShaderCode = QStringLiteral(R"--( -uniform highp mat4 matrix; -attribute highp vec4 position; // use vec4 here (will be padded) for matrix multiplication -attribute highp vec2 texcoord; -varying highp vec2 vTexcoord; -void main() -{ - vTexcoord = texcoord; - gl_Position = matrix * position; -} -)--"); - - QString fragmentShaderCode = QStringLiteral(R"--( -uniform sampler2D texture; -uniform highp vec4 color; -varying highp vec2 vTexcoord; -void main() -{ - gl_FragColor = texture2D(texture, vTexcoord) * color; -} -)--"); - - load(vertexShaderCode, fragmentShaderCode); - - m_matrixLocation = uniformLocation("matrix"); - m_positionLocation = attributeLocation("position"); - m_texcoordLocation = attributeLocation("texcoord"); - m_textureLocation = uniformLocation("texture"); - m_colorLocation = uniformLocation("color"); -} diff --git a/src/shaders/coloredtextureshader.h b/src/shaders/coloredtextureshader.h deleted file mode 100644 index a25df8106d0..00000000000 --- a/src/shaders/coloredtextureshader.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "shaders/shader.h" - -namespace mixxx { -class ColoredTextureShader; -} - -class mixxx::ColoredTextureShader final : public mixxx::Shader { - public: - ColoredTextureShader() = default; - ~ColoredTextureShader() = default; - void init(); - - int matrixLocation() const { - return m_matrixLocation; - } - int positionLocation() const { - return m_positionLocation; - } - int texcoordLocation() const { - return m_texcoordLocation; - } - int textureLocation() const { - return m_textureLocation; - } - int colorLocation() const { - return m_colorLocation; - } - - private: - int m_matrixLocation; - int m_positionLocation; - int m_texcoordLocation; - int m_textureLocation; - int m_colorLocation; - - DISALLOW_COPY_AND_ASSIGN(ColoredTextureShader) -}; diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 6f3c503b448..42cdf8efbcc 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -9,7 +9,6 @@ #include #include -#include "util/texture.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -35,7 +34,7 @@ void allshader::DigitsRenderer::init() { } float allshader::DigitsRenderer::height() const { - return static_cast(m_pTexture->height()); + return static_cast(m_texture.height()); } void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float devicePixelRatio) { @@ -116,14 +115,13 @@ void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float deviceP painter.end(); - m_pTexture = createTexture(image); + m_texture.setData(image); } float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, float x, float y, const QString& s, - QColor color, float devicePixelRatio) { const int n = s.length(); const float x0 = x; @@ -151,10 +149,8 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, const int textureLocation = m_shader.uniformLocation("texture"); const int positionLocation = m_shader.attributeLocation("position"); const int texcoordLocation = m_shader.attributeLocation("texcoord"); - const int colorLocation = m_shader.colorLocation(); m_shader.setUniformValue(matrixLocation, matrix); - m_shader.setUniformValue(colorLocation, color); m_shader.enableAttributeArray(positionLocation); m_shader.setAttributeArray( @@ -165,11 +161,11 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, m_shader.setUniformValue(textureLocation, 0); - m_pTexture->bind(); + m_texture.bind(); glDrawArrays(GL_TRIANGLES, 0, posVertices.size()); - m_pTexture->release(); + m_texture.release(); m_shader.disableAttributeArray(positionLocation); m_shader.disableAttributeArray(texcoordLocation); diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 634012d29f5..325c79070b4 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -2,9 +2,8 @@ #include -#include "shaders/coloredtextureshader.h" - -class QOpenGLTexture; +#include "shaders/textureshader.h" +#include "util/opengltexture2d.h" namespace allshader { class DigitsRenderer; @@ -21,13 +20,12 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { float x, float y, const QString& s, - QColor color, float devicePixelRatio); float height() const; private: - mixxx::ColoredTextureShader m_shader; - std::unique_ptr m_pTexture; + mixxx::TextureShader m_shader; + OpenGLTexture2D m_texture; float m_offset[13]; float m_width[12]; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 32446326976..40564e316e8 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -292,12 +292,12 @@ void allshader::WaveformRenderMark::paintGL() { const float markHalfWidth = m_playPosMarkTexture.width() / devicePixelRatio / 2.f; const float drawOffset = currentMarkPoint - markHalfWidth; - drawTexture(matrix, drawOffset, 0.f, m_pPlayPosMarkTexture.get()); + drawTexture(matrix, drawOffset, 0.f, &m_playPosMarkTexture); } if (WaveformWidgetFactory::instance()->getUntilNextMarkerShow() != UntilNextMarkerShow::None) { updateUntilMark(playPosition, nextMarkPosition); - drawUntilMark(matrix, drawOffset + 20); + drawUntilMark(matrix, currentMarkPoint + 20); } } @@ -316,8 +316,6 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa if (m_timeUntilMark == 0.0) { return; } - const int ialpha = 255; - const float ch = m_digitsRenderer.height() / devicePixelRatio; float y = untilNextMarkerAlign == Qt::AlignTop ? 0.f @@ -343,7 +341,6 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa x, y, QString::number(m_beatsUntilMark), - QColor(255, 255, 255, ialpha), devicePixelRatio); if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime) { x += w + std::round(static_cast(m_untilNextMarkerSize) * 0.75f); @@ -360,10 +357,7 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa x, y, timeToString(m_timeUntilMark), ->>>>>>> 13349d0e47 (also show time until marker) - QColor(255, 255, 255, ialpha), devicePixelRatio); ->>>>>>> ea0a4d064d (show beats until next marker, using digitsrenderer with digits texture (generated) and fade out during beat duration) } } diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 5125b1ebadc..d7f525cfa4e 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -52,8 +52,6 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, void drawMark(const QMatrix4x4& matrix, const QRectF& rect, QColor color); void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); - - void updateBeatDistance(double playPosition, double markerPosition); void updateUntilMark(double playPosition, double markerPosition); void drawUntilMark(const QMatrix4x4& matrix, float x); @@ -61,7 +59,8 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, mixxx::TextureShader m_textureShader; OpenGLTexture2D m_playPosMarkTexture; DigitsRenderer m_digitsRenderer; - int m_beatDistance; + int m_beatsUntilMark; + double m_timeUntilMark; double m_currentBeatPosition; double m_nextBeatPosition; int m_untilNextMarkerSize; From 8d3a5599734d889262b3408b060ada75b9c172c4 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 20 Apr 2024 00:39:39 +0200 Subject: [PATCH 09/30] comments and code clarity in response to review --- .../renderers/allshader/digitsrenderer.cpp | 61 +++++++++++++------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 42cdf8efbcc..c4321e2959e 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -15,6 +15,19 @@ // Render digits using a texture (generated) with digits with blurred dark outline namespace { + +// The texture will contain 12 characters: 10 digits, colon and dot +constexpr int NUM_CHARS = 12; + +// space around chars for blurred dark outline +constexpr qreal SPACE = 3; +// extra kerning when rendering chars +constexpr qreal KERNING = 1; + +char indexToChar(int index) { + const char* str = "0123456789:."; + return str[index]; +} int charToIndex(char ch) { if (ch >= '0' && ch <= '9') { return static_cast(ch - '0'); @@ -22,7 +35,11 @@ int charToIndex(char ch) { if (ch == ':') { return 10; } - return 11; + if (ch == '.') { + return 11; + } + assert(false); + return 11; // fallback to dot } } // namespace @@ -39,7 +56,6 @@ float allshader::DigitsRenderer::height() const { void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float devicePixelRatio) { QFont font; - const char* str = "0123456789:."; font.setFamily("Open Sans"); font.setPixelSize(fontPixelSize); @@ -47,18 +63,18 @@ void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float deviceP qreal totalTextWidth = 0; qreal maxTextHeight = 0; - for (int i = 0; i < 12; i++) { - assert(charToIndex(str[i]) == i); - const QString text(str[i]); + for (int i = 0; i < NUM_CHARS; i++) { + assert(charToIndex(indexToChar(i)) == i); + const QString text(indexToChar(i)); const auto rect = metrics.tightBoundingRect(text); maxTextHeight = std::max(maxTextHeight, rect.height()); - totalTextWidth += metrics.horizontalAdvance(text) + 6; + totalTextWidth += metrics.horizontalAdvance(text) + SPACE + SPACE; } totalTextWidth = std::ceil(totalTextWidth); QImage image(static_cast(totalTextWidth * devicePixelRatio), - static_cast(std::ceil(maxTextHeight + 6) * devicePixelRatio), + static_cast(std::ceil(maxTextHeight + SPACE + SPACE) * devicePixelRatio), QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(devicePixelRatio); image.fill(Qt::transparent); @@ -75,12 +91,12 @@ void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float deviceP painter.setPen(pen); painter.setFont(font); qreal x = 0; - for (int i = 0; i < 12; i++) { - const QString text(str[i]); + for (int i = 0; i < NUM_CHARS; i++) { + const QString text(indexToChar(i)); QPainterPath path; - path.addText(QPointF(x + 3, maxTextHeight + 3), font, text); + path.addText(QPointF(x + SPACE, maxTextHeight + SPACE), font, text); painter.drawPath(path); - x += metrics.horizontalAdvance(text) + 6; + x += metrics.horizontalAdvance(text) + SPACE + SPACE; } painter.end(); @@ -104,14 +120,15 @@ void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float deviceP painter.setFont(font); x = 0; - for (int i = 0; i < 12; i++) { - const QString text(str[i]); - painter.drawText(QPointF(x + 3, maxTextHeight + 3), text); + for (int i = 0; i < NUM_CHARS; i++) { + const QString text(indexToChar(i)); + painter.drawText(QPointF(x + SPACE, maxTextHeight + SPACE), text); + // position and width of character at index i in the texture m_offset[i] = static_cast(x / totalTextWidth); - m_width[i] = static_cast(metrics.horizontalAdvance(text) + 6); - x += metrics.horizontalAdvance(text) + 6; + m_width[i] = static_cast(metrics.horizontalAdvance(text) + SPACE + SPACE); + x += metrics.horizontalAdvance(text) + SPACE + SPACE; } - m_offset[12] = 1.f; + m_offset[NUM_CHARS] = 1.f; painter.end(); @@ -125,6 +142,7 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, float devicePixelRatio) { const int n = s.length(); const float x0 = x; + const float dx = static_cast(-(SPACE + SPACE) + KERNING); VertexData posVertices; VertexData texVertices; @@ -132,15 +150,18 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, posVertices.reserve(n * 6); // two triangles per character texVertices.reserve(n * 6); - for (int i = 0; i < n; i++) { - int index = charToIndex(s[i].cell()); + const auto byteArray = s.toUtf8(); + + for (const auto c : s.toUtf8()) { + // add vertices for the rectangles in the texture corresponding with each char in QString s + int index = charToIndex(c); texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); posVertices.addRectangle(x, y, x + m_width[index], y + height() / devicePixelRatio); - x += m_width[index] - 5.f; + x += m_width[index] + dx; } m_shader.bind(); From 27845677c5ed01cec6140f55afd8b3c1c8dd8dcc Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 20 Apr 2024 01:26:57 +0200 Subject: [PATCH 10/30] removed unused var --- src/waveform/renderers/allshader/digitsrenderer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index c4321e2959e..a2db96768d4 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -150,8 +150,6 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, posVertices.reserve(n * 6); // two triangles per character texVertices.reserve(n * 6); - const auto byteArray = s.toUtf8(); - for (const auto c : s.toUtf8()) { // add vertices for the rectangles in the texture corresponding with each char in QString s int index = charToIndex(c); From 3e6138692ec669bbe63ae095c3436b3379704bc8 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 20 Apr 2024 02:08:24 +0200 Subject: [PATCH 11/30] avoid -Wclazy-range-loop-detach --- src/waveform/renderers/allshader/digitsrenderer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index a2db96768d4..585c3555a71 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -150,9 +150,11 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, posVertices.reserve(n * 6); // two triangles per character texVertices.reserve(n * 6); - for (const auto c : s.toUtf8()) { + const QByteArray byteArray = s.toUtf8(); + + for (qsizetype i = 0; i < byteArray.size(); i++) { // add vertices for the rectangles in the texture corresponding with each char in QString s - int index = charToIndex(c); + int index = charToIndex(byteArray[i]); texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); posVertices.addRectangle(x, From 1d07cc1b60dd2862c980438412bd9c6611b9f8b8 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 20 Apr 2024 15:33:41 +0200 Subject: [PATCH 12/30] decide which markers should be included in the beats/time until next marker display --- .../allshader/waveformrendermark.cpp | 4 +++- src/waveform/renderers/waveformmark.cpp | 20 ++++++++++++++++++- src/waveform/renderers/waveformmark.h | 6 ++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 40564e316e8..e4a79f20871 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -225,7 +225,9 @@ void allshader::WaveformRenderMark::paintGL() { samplePosition, positionType)) * devicePixelRatio) / devicePixelRatio; - if (samplePosition >= playPosition + 1.0 && samplePosition < nextMarkPosition) { + if (pMark->isShowUntilNext() && + samplePosition >= playPosition + 1.0 && + samplePosition < nextMarkPosition) { nextMarkPosition = samplePosition; } const double sampleEndPosition = pMark->getSampleEndPosition(); diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index e1174f87638..1f86e4008e5 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -67,6 +67,17 @@ float overlappingMarkerIncrement(const float labelRectHeight, const float breadt return std::max(minIncrement, fullIncrement - std::max(0.f, threshold - breadth)); } +bool isShowUntilNextPositionControl(const QString& positionControl) { + // To identify which markers are included in the beat/time until next marker + // display, in addition to the hotcues + static const QStringList list{{"cue_point", + "intro_start_position", + "intro_end_position", + "outro_start_position", + "outro_end_position"}}; + return list.contains(positionControl); +} + } // anonymous namespace WaveformMark::WaveformMark(const QString& group, @@ -75,14 +86,21 @@ WaveformMark::WaveformMark(const QString& group, int priority, const WaveformSignalColors& signalColors, int hotCue) - : m_linePosition{}, m_breadth{}, m_level{}, m_iPriority(priority), m_iHotCue(hotCue) { + : m_linePosition{}, + m_breadth{}, + m_level{}, + m_iPriority(priority), + m_iHotCue(hotCue), + m_showUntilNext{} { QString positionControl; QString endPositionControl; if (hotCue != Cue::kNoHotCue) { positionControl = "hotcue_" + QString::number(hotCue + 1) + "_position"; endPositionControl = "hotcue_" + QString::number(hotCue + 1) + "_endposition"; + m_showUntilNext = true; } else { positionControl = context.selectString(node, "Control"); + m_showUntilNext = isShowUntilNextPositionControl(positionControl); } if (!positionControl.isEmpty()) { diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index 8929ba74cb2..b604f5eb83e 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -82,6 +82,9 @@ class WaveformMark { } return m_pVisibleCO->toBool(); } + bool isShowUntilNext() const { + return m_showUntilNext; + } template void connectVisibleChanged(Receiver receiver, Slot slot) const { @@ -167,6 +170,9 @@ class WaveformMark { int m_iPriority; int m_iHotCue; + // Whether this marker is used in the show beats/time until next marker display. + bool m_showUntilNext; + QColor m_fillColor; QColor m_borderColor; QColor m_labelColor; From bd80bb1edbade0534c91a121fb0f08602727563a Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 20 Apr 2024 16:04:32 +0200 Subject: [PATCH 13/30] show until next marker options only when waveform type supports it --- src/preferences/dialog/dlgprefwaveform.cpp | 12 ++++++++++++ src/preferences/dialog/dlgprefwaveform.h | 1 + src/preferences/dialog/dlgprefwaveformdlg.ui | 17 +++++++++++++---- src/waveform/waveformwidgetfactory.cpp | 18 ++++++++++++++++++ src/waveform/waveformwidgetfactory.h | 1 + 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 89eead0805b..0f03b20f8ad 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -176,6 +176,8 @@ void DlgPrefWaveform::slotUpdate() { waveformTypeComboBox->setCurrentIndex(currentIndex); } + updateEnableUntilNextMarker(); + frameRateSpinBox->setValue(factory->getFrameRate()); frameRateSlider->setValue(factory->getFrameRate()); endOfTrackWarningTimeSpinBox->setValue(factory->getEndOfTrackWarningTime()); @@ -282,6 +284,16 @@ void DlgPrefWaveform::slotSetWaveformType(int index) { } int handleIndex = waveformTypeComboBox->itemData(index).toInt(); WaveformWidgetFactory::instance()->setWidgetTypeFromHandle(handleIndex); + + updateEnableUntilNextMarker(); +} + +void DlgPrefWaveform::updateEnableUntilNextMarker() { + const bool enabled = WaveformWidgetFactory::instance()->widgetTypeSupportsUntilNextMarker(); + untilNextMarkerAlignComboBox->setEnabled(enabled); + untilNextMarkerShowComboBox->setEnabled(enabled); + untilNextMarkerSizeSpinBox->setEnabled(enabled); + requiresGLSLLabel->setVisible(!enabled); } void DlgPrefWaveform::slotSetWaveformOverviewType(int index) { diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index 03f70aba2a4..7eaad3f3529 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -48,6 +48,7 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void initWaveformControl(); void calculateCachedWaveformDiskUsage(); void notifyRebootNecessary(); + void updateEnableUntilNextMarker(); UserSettingsPointer m_pConfig; std::shared_ptr m_pLibrary; diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 3a13ebe75b7..3c6c6124204 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -532,8 +532,17 @@ Select from different types of displays for the waveform, which differ primarily - - + + + + This functionality requires a waveform type marked "(GLSL)". + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Caching @@ -543,7 +552,7 @@ Select from different types of displays for the waveform, which differ primarily - + @@ -595,7 +604,7 @@ Select from different types of displays for the waveform, which differ primarily - + OpenGL status diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 50e689d5df2..3fc4d4cda63 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -542,6 +542,24 @@ bool WaveformWidgetFactory::setWidgetType( return isAcceptable; } +bool WaveformWidgetFactory::widgetTypeSupportsUntilNextMarker() const { + switch (m_configType) { + case WaveformWidgetType::AllShaderRGBWaveform: + return true; + case WaveformWidgetType::AllShaderLRRGBWaveform: + return true; + case WaveformWidgetType::AllShaderFilteredWaveform: + return true; + case WaveformWidgetType::AllShaderSimpleWaveform: + return true; + case WaveformWidgetType::AllShaderHSVWaveform: + return true; + default: + break; + } + return false; +} + bool WaveformWidgetFactory::setWidgetTypeFromConfig() { int empty = findHandleIndexFromType(WaveformWidgetType::EmptyWaveform); int desired = findHandleIndexFromType(m_configType); diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index d63f4a53c68..3950fcc4fb0 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -105,6 +105,7 @@ class WaveformWidgetFactory : public QObject, public Singleton Date: Sun, 21 Apr 2024 00:26:33 +0200 Subject: [PATCH 14/30] added isValid check --- .../allshader/waveformrendermark.cpp | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index e4a79f20871..7b88a669b91 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -210,11 +210,34 @@ void allshader::WaveformRenderMark::paintGL() { double nextMarkPosition = std::numeric_limits::max(); for (const auto& pMark : std::as_const(m_marks)) { + if (!pMark->isValid()) { + continue; + } + + const double samplePosition = pMark->getSamplePosition(); + + if (samplePosition == Cue::kNoPosition) { + continue; + } + QOpenGLTexture* pTexture = static_cast(pMark->m_pGraphics.get()) ->texture(); - const double samplePosition = pMark->getSamplePosition(); + const float currentMarkPoint = + std::round( + static_cast( + m_waveformRenderer + ->transformSamplePositionInRendererWorld( + samplePosition)) * + devicePixelRatio) / + devicePixelRatio; + if (pMark->isShowUntilNext() && + samplePosition >= playPosition + 1.0 && + samplePosition < nextMarkPosition) { + nextMarkPosition = samplePosition; + } + const double sampleEndPosition = pMark->getSampleEndPosition(); if (samplePosition != Cue::kNoPosition) { const float currentMarkPoint = @@ -232,12 +255,6 @@ void allshader::WaveformRenderMark::paintGL() { } const double sampleEndPosition = pMark->getSampleEndPosition(); - // Pixmaps are expected to have the mark stroke at the center, - // and preferably have an odd width in order to have the stroke - // exactly at the sample position. - const float markHalfWidth = pTexture->width() / devicePixelRatio / 2.f; - const float drawOffset = currentMarkPoint - markHalfWidth; - bool visible = false; // Check if the current point needs to be displayed. if (drawOffset > -markHalfWidth && From e9fe5b7f63a35b308192914e490c8ea1e4a82ae8 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sat, 4 May 2024 00:14:33 +0200 Subject: [PATCH 15/30] improve timeSecToString function and arg name, typo fix, using asprintf for time formatting, iterate over string, without bytearray, improved code to check if marker is in list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Schürmann Co-authored-by: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> --- .../renderers/allshader/digitsrenderer.cpp | 14 ++++++-------- .../renderers/allshader/waveformrendermark.cpp | 14 ++++++-------- src/waveform/renderers/waveformmark.cpp | 15 +++++++++------ src/waveform/waveformwidgetfactory.cpp | 3 +-- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 585c3555a71..bf3c9f15a87 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -28,9 +28,10 @@ char indexToChar(int index) { const char* str = "0123456789:."; return str[index]; } -int charToIndex(char ch) { - if (ch >= '0' && ch <= '9') { - return static_cast(ch - '0'); +int charToIndex(QChar ch) { + int value = ch.digitValue(); + if (value != -1) { + return value; } if (ch == ':') { return 10; @@ -150,11 +151,8 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, posVertices.reserve(n * 6); // two triangles per character texVertices.reserve(n * 6); - const QByteArray byteArray = s.toUtf8(); - - for (qsizetype i = 0; i < byteArray.size(); i++) { - // add vertices for the rectangles in the texture corresponding with each char in QString s - int index = charToIndex(byteArray[i]); + for (QChar c : s) { + int index = charToIndex(c); texVertices.addRectangle(m_offset[index], 0.f, m_offset[index + 1], 1.f); posVertices.addRectangle(x, diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 7b88a669b91..49cd6e7cd46 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -46,16 +46,14 @@ class TextureGraphics : public WaveformMark::Graphics { // that updateMarkImages should not be called immediately. namespace { -QString timeToString(double t) { - int hundreds = std::lround(t * 100.0); - int seconds = hundreds / 100; - hundreds -= seconds * 100; +QString timeSecToString(double timeSec) { + int hundredths = std::lround(timeSec * 100.0); + int seconds = hundredths / 100; + hundredths -= seconds * 100; int minutes = seconds / 60; seconds -= minutes * 60; - return QString::number(minutes) + (seconds < 10 ? ":0" : ":") + - QString::number(seconds) + (hundreds < 10 ? ".0" : ".") + - QString::number(hundreds); + return QString::asprintf("%d:%02d.%02d", minutes, seconds, hundredths); } } // namespace @@ -375,7 +373,7 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa m_digitsRenderer.draw(matrix, x, y, - timeToString(m_timeUntilMark), + timeSecToString(m_timeUntilMark), devicePixelRatio); } } diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 1f86e4008e5..a34127ef7fd 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -70,12 +70,15 @@ float overlappingMarkerIncrement(const float labelRectHeight, const float breadt bool isShowUntilNextPositionControl(const QString& positionControl) { // To identify which markers are included in the beat/time until next marker // display, in addition to the hotcues - static const QStringList list{{"cue_point", - "intro_start_position", - "intro_end_position", - "outro_start_position", - "outro_end_position"}}; - return list.contains(positionControl); + using namespace Qt::Literals::StringLiterals; + constexpr std::array list = {"cue_point"_L1, + "intro_start_position"_L1, + "intro_end_position"_L1, + "outro_start_position"_L1, + "outro_end_position"_L1}; + return std::any_of(list.cbegin(), list.cend(), [positionControl](auto& view) { + return view == positionControl; + }); } } // anonymous namespace diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 3fc4d4cda63..9a71009507e 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -1283,8 +1283,7 @@ QSurfaceFormat WaveformWidgetFactory::getSurfaceFormat(UserSettingsPointer confi void WaveformWidgetFactory::setUntilNextMarkerShow(UntilNextMarkerShow value) { m_untilNextMarkerShow = value; if (m_config) { - m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerShow"), - static_cast(m_untilNextMarkerShow)); + m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerShow"), m_untilNextMarkerShow); } } void WaveformWidgetFactory::setUntilNextMarkerAlign(Qt::Alignment align) { From eeb092d9d41e2740643511cf738d5077de9c8261 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sat, 4 May 2024 00:33:23 +0200 Subject: [PATCH 16/30] use make_unique Co-authored-by: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> --- src/waveform/renderers/allshader/digitsrenderer.cpp | 4 ++-- src/waveform/renderers/allshader/waveformrendermark.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index bf3c9f15a87..6c44f6d6067 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -103,13 +103,13 @@ void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float deviceP // Apply Gaussian blur to dark outline - QGraphicsBlurEffect* blur = new QGraphicsBlurEffect; // ownership passed to item + auto blur = std::make_unique(); blur->setBlurRadius(3); QGraphicsScene scene; QGraphicsPixmapItem item; item.setPixmap(QPixmap::fromImage(image)); - item.setGraphicsEffect(blur); + item.setGraphicsEffect(blur.release()); scene.addItem(&item); image.fill(Qt::transparent); diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 49cd6e7cd46..01d109a46e7 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -68,8 +68,8 @@ allshader::WaveformRenderMark::WaveformRenderMark( } bool allshader::WaveformRenderMark::init() { - m_pTimeRemainingControl.reset(new ControlProxy( - m_waveformRenderer->getGroup(), "time_remaining")); + m_pTimeRemainingControl = std::make_unique( + m_waveformRenderer->getGroup(), "time_remaining"); return true; } From 007a19e7c32c6e9db2797feb8b0a18559c6da4ae Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 4 May 2024 00:45:02 +0200 Subject: [PATCH 17/30] improved naming UntilMarkTextPixelSize --- src/preferences/dialog/dlgprefwaveform.cpp | 12 ++++++------ src/preferences/dialog/dlgprefwaveform.h | 2 +- src/preferences/dialog/dlgprefwaveformdlg.ui | 6 +++--- .../renderers/allshader/waveformrendermark.cpp | 15 ++++++++------- .../renderers/allshader/waveformrendermark.h | 2 +- src/waveform/waveformwidgetfactory.cpp | 15 ++++++++------- src/waveform/waveformwidgetfactory.h | 8 ++++---- 7 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 0f03b20f8ad..0ebd905dc59 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -150,10 +150,10 @@ DlgPrefWaveform::DlgPrefWaveform( QOverload::of(&QComboBox::currentIndexChanged), this, &DlgPrefWaveform::slotSetUntilNextMarkerAlign); - connect(untilNextMarkerSizeSpinBox, + connect(untilMarkTextPixelSizeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, - &DlgPrefWaveform::slotSetUntilNextMarkerSize); + &DlgPrefWaveform::slotSetUntilMarkTextPixelSize); setScrollSafeGuardForAllInputWidgets(this); } @@ -199,7 +199,7 @@ void DlgPrefWaveform::slotUpdate() { untilNextMarkerAlignComboBox->setCurrentIndex( WaveformWidgetFactory::toUntilNextMarkerAlignIndex( factory->getUntilNextMarkerAlign())); - untilNextMarkerSizeSpinBox->setValue(factory->getUntilNextMarkerSize()); + untilMarkTextPixelSizeSpinBox->setValue(factory->getUntilMarkTextPixelSize()); // By default we set RGB woverview = "2" int overviewType = m_pConfig->getValue( @@ -292,7 +292,7 @@ void DlgPrefWaveform::updateEnableUntilNextMarker() { const bool enabled = WaveformWidgetFactory::instance()->widgetTypeSupportsUntilNextMarker(); untilNextMarkerAlignComboBox->setEnabled(enabled); untilNextMarkerShowComboBox->setEnabled(enabled); - untilNextMarkerSizeSpinBox->setEnabled(enabled); + untilMarkTextPixelSizeSpinBox->setEnabled(enabled); requiresGLSLLabel->setVisible(!enabled); } @@ -364,8 +364,8 @@ void DlgPrefWaveform::slotSetUntilNextMarkerAlign(int index) { WaveformWidgetFactory::toUntilNextMarkerAlign(index)); } -void DlgPrefWaveform::slotSetUntilNextMarkerSize(int value) { - WaveformWidgetFactory::instance()->setUntilNextMarkerSize(value); +void DlgPrefWaveform::slotSetUntilMarkTextPixelSize(int value) { + WaveformWidgetFactory::instance()->setUntilMarkTextPixelSize(value); } void DlgPrefWaveform::calculateCachedWaveformDiskUsage() { diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index 7eaad3f3529..e4726c6f508 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -40,7 +40,7 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotSetPlayMarkerPosition(int position); void slotSetUntilNextMarkerShow(int index); void slotSetUntilNextMarkerAlign(int index); - void slotSetUntilNextMarkerSize(int value); + void slotSetUntilMarkTextPixelSize(int value); signals: void reloadUserInterface(); diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 3c6c6124204..85f1eb012cc 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -501,7 +501,7 @@ Select from different types of displays for the waveform, which differ primarily - + Size @@ -509,12 +509,12 @@ Select from different types of displays for the waveform, which differ primarily Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - untilNextMarkerSizeSpinBox + untilMarkTextPixelSizeSpinBox - + diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 01d109a46e7..e54f6d98d01 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -82,9 +82,9 @@ void allshader::WaveformRenderMark::initializeGL() { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); - m_untilNextMarkerSize = WaveformWidgetFactory::instance()->getUntilNextMarkerSize(); + m_untilMarkTextPixelSize = WaveformWidgetFactory::instance()->getUntilMarkTextPixelSize(); m_digitsRenderer.generateTexture( - m_untilNextMarkerSize, m_waveformRenderer->getDevicePixelRatio()); + m_untilMarkTextPixelSize, m_waveformRenderer->getDevicePixelRatio()); } void allshader::WaveformRenderMark::drawTexture( @@ -323,10 +323,11 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa const auto untilNextMarkerAlign = WaveformWidgetFactory::instance()->getUntilNextMarkerAlign(); const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const int untilNextMarkerSize = WaveformWidgetFactory::instance()->getUntilNextMarkerSize(); - if (untilNextMarkerSize != m_untilNextMarkerSize) { - m_untilNextMarkerSize = untilNextMarkerSize; - m_digitsRenderer.generateTexture(m_untilNextMarkerSize, + const int untilMarkTextPixelSize = + WaveformWidgetFactory::instance()->getUntilMarkTextPixelSize(); + if (untilMarkTextPixelSize != m_untilMarkTextPixelSize) { + m_untilMarkTextPixelSize = untilMarkTextPixelSize; + m_digitsRenderer.generateTexture(m_untilMarkTextPixelSize, m_waveformRenderer->getDevicePixelRatio()); } @@ -360,7 +361,7 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa QString::number(m_beatsUntilMark), devicePixelRatio); if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime) { - x += w + std::round(static_cast(m_untilNextMarkerSize) * 0.75f); + x += w + std::round(static_cast(m_untilMarkTextPixelSize) * 0.75f); } if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { y += ch; diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index d7f525cfa4e..a7b3a31a54d 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -63,7 +63,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, double m_timeUntilMark; double m_currentBeatPosition; double m_nextBeatPosition; - int m_untilNextMarkerSize; + int m_untilMarkTextPixelSize; std::unique_ptr m_pTimeRemainingControl; bool m_isSlipRenderer; diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 9a71009507e..637af0c6d58 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -113,7 +113,7 @@ WaveformWidgetFactory::WaveformWidgetFactory() m_overviewNormalized(false), m_untilNextMarkerShow(UntilNextMarkerShow::None), m_untilNextMarkerAlign(Qt::AlignVCenter), - m_untilNextMarkerSize(24), + m_untilMarkTextPixelSize(24), m_openGlAvailable(false), m_openGlesAvailable(false), m_openGLShaderAvailable(false), @@ -412,9 +412,9 @@ bool WaveformWidgetFactory::setConfig(UserSettingsPointer config) { setUntilNextMarkerAlign(toUntilNextMarkerAlign( m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerAlign"), toUntilNextMarkerAlignIndex(m_untilNextMarkerAlign)))); - setUntilNextMarkerSize( - m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerSize"), - m_untilNextMarkerSize)); + setUntilMarkTextPixelSize( + m_config->getValue(ConfigKey("[Waveform]", "UntilMarkTextPixelSize"), + m_untilMarkTextPixelSize)); return true; } @@ -1293,10 +1293,11 @@ void WaveformWidgetFactory::setUntilNextMarkerAlign(Qt::Alignment align) { toUntilNextMarkerAlignIndex(m_untilNextMarkerAlign)); } } -void WaveformWidgetFactory::setUntilNextMarkerSize(int value) { - m_untilNextMarkerSize = value; +void WaveformWidgetFactory::setUntilMarkTextPixelSize(int value) { + m_untilMarkTextPixelSize = value; if (m_config) { - m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerSize"), m_untilNextMarkerSize); + m_config->setValue(ConfigKey("[Waveform]", "UntilMarkTextPixelSize"), + m_untilMarkTextPixelSize); } } diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 3950fcc4fb0..5b9e0de583a 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -109,7 +109,7 @@ class WaveformWidgetFactory : public QObject, public Singleton Date: Sat, 4 May 2024 02:17:57 +0200 Subject: [PATCH 18/30] waveform untilmark preference improvements --- src/preferences/dialog/dlgprefwaveform.cpp | 66 ++++---- src/preferences/dialog/dlgprefwaveform.h | 7 +- src/preferences/dialog/dlgprefwaveformdlg.ui | 148 +++++++++++------- .../allshader/waveformrendermark.cpp | 33 ++-- src/waveform/waveformwidgetfactory.cpp | 65 +++++--- src/waveform/waveformwidgetfactory.h | 35 ++--- 6 files changed, 206 insertions(+), 148 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 0ebd905dc59..70b30d05e9b 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -41,16 +41,10 @@ DlgPrefWaveform::DlgPrefWaveform( defaultZoomComboBox->addItem(QString::number(100 / static_cast(i), 'f', 1) + " %"); } - // Populate untilNextMarker options - untilNextMarkerShowComboBox->addItem(tr("None")); - untilNextMarkerShowComboBox->addItem(tr("Beats")); - untilNextMarkerShowComboBox->addItem(tr("Time")); - untilNextMarkerShowComboBox->addItem(tr("Beats + Time")); - untilNextMarkerShowComboBox->addItem(tr("Beats + Time (multi-line)")); - - untilNextMarkerAlignComboBox->addItem(tr("Top")); - untilNextMarkerAlignComboBox->addItem(tr("Center")); - untilNextMarkerAlignComboBox->addItem(tr("Bottom")); + // Populate untilMark options + untilMarkAlignComboBox->addItem(tr("Top")); + untilMarkAlignComboBox->addItem(tr("Center")); + untilMarkAlignComboBox->addItem(tr("Bottom")); // The GUI is not fully setup so connecting signals before calling // slotUpdate can generate rebootMixxxView calls. @@ -142,14 +136,18 @@ DlgPrefWaveform::DlgPrefWaveform( &QSlider::valueChanged, this, &DlgPrefWaveform::slotSetPlayMarkerPosition); - connect(untilNextMarkerShowComboBox, - QOverload::of(&QComboBox::currentIndexChanged), + connect(untilMarkShowBeatsCheckBox, + &QCheckBox::toggled, this, - &DlgPrefWaveform::slotSetUntilNextMarkerShow); - connect(untilNextMarkerAlignComboBox, + &DlgPrefWaveform::slotSetUntilMarkShowBeats); + connect(untilMarkShowTimeCheckBox, + &QCheckBox::toggled, + this, + &DlgPrefWaveform::slotSetUntilMarkShowTime); + connect(untilMarkAlignComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, - &DlgPrefWaveform::slotSetUntilNextMarkerAlign); + &DlgPrefWaveform::slotSetUntilMarkAlign); connect(untilMarkTextPixelSizeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, @@ -176,7 +174,7 @@ void DlgPrefWaveform::slotUpdate() { waveformTypeComboBox->setCurrentIndex(currentIndex); } - updateEnableUntilNextMarker(); + updateEnableUntilMark(); frameRateSpinBox->setValue(factory->getFrameRate()); frameRateSlider->setValue(factory->getFrameRate()); @@ -194,11 +192,11 @@ void DlgPrefWaveform::slotUpdate() { beatGridAlphaSpinBox->setValue(factory->getBeatGridAlpha()); beatGridAlphaSlider->setValue(factory->getBeatGridAlpha()); - untilNextMarkerShowComboBox->setCurrentIndex( - static_cast(factory->getUntilNextMarkerShow())); - untilNextMarkerAlignComboBox->setCurrentIndex( - WaveformWidgetFactory::toUntilNextMarkerAlignIndex( - factory->getUntilNextMarkerAlign())); + untilMarkShowBeatsCheckBox->setChecked(factory->getUntilMarkShowBeats()); + untilMarkShowTimeCheckBox->setChecked(factory->getUntilMarkShowTime()); + untilMarkAlignComboBox->setCurrentIndex( + WaveformWidgetFactory::toUntilMarkAlignIndex( + factory->getUntilMarkAlign())); untilMarkTextPixelSizeSpinBox->setValue(factory->getUntilMarkTextPixelSize()); // By default we set RGB woverview = "2" @@ -285,13 +283,14 @@ void DlgPrefWaveform::slotSetWaveformType(int index) { int handleIndex = waveformTypeComboBox->itemData(index).toInt(); WaveformWidgetFactory::instance()->setWidgetTypeFromHandle(handleIndex); - updateEnableUntilNextMarker(); + updateEnableUntilMark(); } -void DlgPrefWaveform::updateEnableUntilNextMarker() { - const bool enabled = WaveformWidgetFactory::instance()->widgetTypeSupportsUntilNextMarker(); - untilNextMarkerAlignComboBox->setEnabled(enabled); - untilNextMarkerShowComboBox->setEnabled(enabled); +void DlgPrefWaveform::updateEnableUntilMark() { + const bool enabled = WaveformWidgetFactory::instance()->widgetTypeSupportsUntilMark(); + untilMarkShowBeatsCheckBox->setEnabled(enabled); + untilMarkShowTimeCheckBox->setEnabled(enabled); + untilMarkAlignComboBox->setEnabled(enabled); untilMarkTextPixelSizeSpinBox->setEnabled(enabled); requiresGLSLLabel->setVisible(!enabled); } @@ -354,14 +353,17 @@ void DlgPrefWaveform::slotSetPlayMarkerPosition(int position) { WaveformWidgetFactory::instance()->setPlayMarkerPosition(position / 100.0); } -void DlgPrefWaveform::slotSetUntilNextMarkerShow(int index) { - WaveformWidgetFactory::instance()->setUntilNextMarkerShow( - static_cast(index)); +void DlgPrefWaveform::slotSetUntilMarkShowBeats(bool checked) { + WaveformWidgetFactory::instance()->setUntilMarkShowBeats(checked); +} + +void DlgPrefWaveform::slotSetUntilMarkShowTime(bool checked) { + WaveformWidgetFactory::instance()->setUntilMarkShowTime(checked); } -void DlgPrefWaveform::slotSetUntilNextMarkerAlign(int index) { - WaveformWidgetFactory::instance()->setUntilNextMarkerAlign( - WaveformWidgetFactory::toUntilNextMarkerAlign(index)); +void DlgPrefWaveform::slotSetUntilMarkAlign(int index) { + WaveformWidgetFactory::instance()->setUntilMarkAlign( + WaveformWidgetFactory::toUntilMarkAlign(index)); } void DlgPrefWaveform::slotSetUntilMarkTextPixelSize(int value) { diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index e4726c6f508..fcf2e13817b 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -38,8 +38,9 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotClearCachedWaveforms(); void slotSetBeatGridAlpha(int alpha); void slotSetPlayMarkerPosition(int position); - void slotSetUntilNextMarkerShow(int index); - void slotSetUntilNextMarkerAlign(int index); + void slotSetUntilMarkShowBeats(bool checked); + void slotSetUntilMarkShowTime(bool checked); + void slotSetUntilMarkAlign(int index); void slotSetUntilMarkTextPixelSize(int value); signals: void reloadUserInterface(); @@ -48,7 +49,7 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void initWaveformControl(); void calculateCachedWaveformDiskUsage(); void notifyRebootNecessary(); - void updateEnableUntilNextMarker(); + void updateEnableUntilMark(); UserSettingsPointer m_pConfig; std::shared_ptr m_pLibrary; diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 85f1eb012cc..c2babe1a8e4 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -459,78 +459,112 @@ Select from different types of displays for the waveform, which differ primarily - + - Until next marker + Play marker hints Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - + + - Show - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - untilNextMarkerShowComboBox + Beats until next marker - - - - - + + - Placement - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - untilNextMarkerAlignComboBox - - - - - - - - - - Size - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - untilMarkTextPixelSizeSpinBox + Time until next marker - - - - - - - px - - - 20 - - - 40 - - - 30 - - + + + + + + Placement + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilMarkAlignComboBox + + + + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + 20 + 20 + + + + + + + + Font size + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilMarkTextPixelSizeSpinBox + + + + + + + + + + px + + + 20 + + + 40 + + + 30 + + + + + + + Qt::Horizontal + + + + 1 + 0 + + + + + diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index e54f6d98d01..3846bf15ef1 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -312,15 +312,17 @@ void allshader::WaveformRenderMark::paintGL() { drawTexture(matrix, drawOffset, 0.f, &m_playPosMarkTexture); } - if (WaveformWidgetFactory::instance()->getUntilNextMarkerShow() != UntilNextMarkerShow::None) { + if (WaveformWidgetFactory::instance()->getUntilMarkShowBeats() || + WaveformWidgetFactory::instance()->getUntilMarkShowTime()) { updateUntilMark(playPosition, nextMarkPosition); drawUntilMark(matrix, currentMarkPoint + 20); } } void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, float x) { - const auto untilNextMarkerShow = WaveformWidgetFactory::instance()->getUntilNextMarkerShow(); - const auto untilNextMarkerAlign = WaveformWidgetFactory::instance()->getUntilNextMarkerAlign(); + const bool untilMarkShowBeats = WaveformWidgetFactory::instance()->getUntilMarkShowBeats(); + const bool untilMarkShowTime = WaveformWidgetFactory::instance()->getUntilMarkShowTime(); + const auto untilMarkAlign = WaveformWidgetFactory::instance()->getUntilMarkAlign(); const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const int untilMarkTextPixelSize = @@ -336,41 +338,34 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa } const float ch = m_digitsRenderer.height() / devicePixelRatio; - float y = untilNextMarkerAlign == Qt::AlignTop ? 0.f - : untilNextMarkerAlign == Qt::AlignBottom + float y = untilMarkAlign == Qt::AlignTop ? 0.f + : untilMarkAlign == Qt::AlignBottom ? m_waveformRenderer->getBreadth() - ch : m_waveformRenderer->getBreadth() / 2.f; - if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { - if (untilNextMarkerAlign != Qt::AlignTop) { + if (untilMarkShowBeats && untilMarkShowTime) { + if (untilMarkAlign != Qt::AlignTop) { y -= ch; } } else { - if (untilNextMarkerAlign != Qt::AlignTop && untilNextMarkerAlign != Qt::AlignBottom) { + if (untilMarkAlign != Qt::AlignTop && untilMarkAlign != Qt::AlignBottom) { // center y -= ch / 2.f; } } - if (untilNextMarkerShow == UntilNextMarkerShow::Beats || - untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime || - untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { - const float w = m_digitsRenderer.draw(matrix, + if (untilMarkShowBeats) { + m_digitsRenderer.draw(matrix, x, y, QString::number(m_beatsUntilMark), devicePixelRatio); - if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime) { - x += w + std::round(static_cast(m_untilMarkTextPixelSize) * 0.75f); - } - if (untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { + if (untilMarkShowTime) { y += ch; } } - if (untilNextMarkerShow == UntilNextMarkerShow::Time || - untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTime || - untilNextMarkerShow == UntilNextMarkerShow::BeatsAndTimeMultiline) { + if (untilMarkShowTime) { m_digitsRenderer.draw(matrix, x, y, diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 637af0c6d58..082ddec19fe 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -111,8 +111,9 @@ WaveformWidgetFactory::WaveformWidgetFactory() m_defaultZoom(WaveformWidgetRenderer::s_waveformDefaultZoom), m_zoomSync(true), m_overviewNormalized(false), - m_untilNextMarkerShow(UntilNextMarkerShow::None), - m_untilNextMarkerAlign(Qt::AlignVCenter), + m_untilMarkShowBeats(false), + m_untilMarkShowTime(false), + m_untilMarkAlign(Qt::AlignVCenter), m_untilMarkTextPixelSize(24), m_openGlAvailable(false), m_openGlesAvailable(false), @@ -406,12 +407,30 @@ bool WaveformWidgetFactory::setConfig(UserSettingsPointer config) { WaveformWidgetRenderer::s_defaultPlayMarkerPosition); setPlayMarkerPosition(m_playMarkerPosition); - setUntilNextMarkerShow(static_cast( - m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerShow"), - static_cast(m_untilNextMarkerShow)))); - setUntilNextMarkerAlign(toUntilNextMarkerAlign( - m_config->getValue(ConfigKey("[Waveform]", "UntilNextMarkerAlign"), - toUntilNextMarkerAlignIndex(m_untilNextMarkerAlign)))); + int untilMarkShowBeats = + m_config->getValueString( + ConfigKey("[Waveform]", "UntilMarkShowBeats")) + .toInt(&ok); + if (ok) { + setUntilMarkShowBeats(static_cast(untilMarkShowBeats)); + } else { + m_config->set(ConfigKey("[Waveform]", "UntilMarkShowBeats"), + ConfigValue(m_untilMarkShowBeats)); + } + int untilMarkShowTime = + m_config->getValueString( + ConfigKey("[Waveform]", "UntilMarkShowTime")) + .toInt(&ok); + if (ok) { + setUntilMarkShowTime(static_cast(untilMarkShowTime)); + } else { + m_config->set(ConfigKey("[Waveform]", "UntilMarkShowTime"), + ConfigValue(m_untilMarkShowTime)); + } + + setUntilMarkAlign(toUntilMarkAlign( + m_config->getValue(ConfigKey("[Waveform]", "UntilMarkAlign"), + toUntilMarkAlignIndex(m_untilMarkAlign)))); setUntilMarkTextPixelSize( m_config->getValue(ConfigKey("[Waveform]", "UntilMarkTextPixelSize"), m_untilMarkTextPixelSize)); @@ -542,7 +561,7 @@ bool WaveformWidgetFactory::setWidgetType( return isAcceptable; } -bool WaveformWidgetFactory::widgetTypeSupportsUntilNextMarker() const { +bool WaveformWidgetFactory::widgetTypeSupportsUntilMark() const { switch (m_configType) { case WaveformWidgetType::AllShaderRGBWaveform: return true; @@ -1280,17 +1299,27 @@ QSurfaceFormat WaveformWidgetFactory::getSurfaceFormat(UserSettingsPointer confi return format; } -void WaveformWidgetFactory::setUntilNextMarkerShow(UntilNextMarkerShow value) { - m_untilNextMarkerShow = value; +void WaveformWidgetFactory::setUntilMarkShowBeats(bool value) { + m_untilMarkShowBeats = value; + if (m_config) { + m_config->set(ConfigKey("[Waveform]", "UntilMarkShowBeats"), + ConfigValue(m_untilMarkShowBeats)); + } +} + +void WaveformWidgetFactory::setUntilMarkShowTime(bool value) { + m_untilMarkShowTime = value; if (m_config) { - m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerShow"), m_untilNextMarkerShow); + m_config->set(ConfigKey("[Waveform]", "UntilMarkShowTime"), + ConfigValue(m_untilMarkShowTime)); } } -void WaveformWidgetFactory::setUntilNextMarkerAlign(Qt::Alignment align) { - m_untilNextMarkerAlign = align; + +void WaveformWidgetFactory::setUntilMarkAlign(Qt::Alignment align) { + m_untilMarkAlign = align; if (m_config) { - m_config->setValue(ConfigKey("[Waveform]", "UntilNextMarkerAlign"), - toUntilNextMarkerAlignIndex(m_untilNextMarkerAlign)); + m_config->setValue(ConfigKey("[Waveform]", "UntilMarkAlign"), + toUntilMarkAlignIndex(m_untilMarkAlign)); } } void WaveformWidgetFactory::setUntilMarkTextPixelSize(int value) { @@ -1302,7 +1331,7 @@ void WaveformWidgetFactory::setUntilMarkTextPixelSize(int value) { } // static -Qt::Alignment WaveformWidgetFactory::toUntilNextMarkerAlign(int index) { +Qt::Alignment WaveformWidgetFactory::toUntilMarkAlign(int index) { switch (index) { case 0: return Qt::AlignTop; @@ -1315,7 +1344,7 @@ Qt::Alignment WaveformWidgetFactory::toUntilNextMarkerAlign(int index) { return Qt::AlignVCenter; } // static -int WaveformWidgetFactory::toUntilNextMarkerAlignIndex(Qt::Alignment align) { +int WaveformWidgetFactory::toUntilMarkAlignIndex(Qt::Alignment align) { switch (align) { case Qt::AlignTop: return 0; diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 5b9e0de583a..cd260871f3f 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -55,14 +55,6 @@ class WaveformWidgetHolder { //######################################## -enum class UntilNextMarkerShow { - None, - Beats, - Time, - BeatsAndTime, - BeatsAndTimeMultiline -}; - class WaveformWidgetFactory : public QObject, public Singleton { Q_OBJECT public: @@ -105,24 +97,28 @@ class WaveformWidgetFactory : public QObject, public Singleton Date: Sat, 4 May 2024 02:24:39 +0200 Subject: [PATCH 19/30] formatting --- src/waveform/renderers/waveformmark.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index a34127ef7fd..dac6ca5b98e 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -72,10 +72,10 @@ bool isShowUntilNextPositionControl(const QString& positionControl) { // display, in addition to the hotcues using namespace Qt::Literals::StringLiterals; constexpr std::array list = {"cue_point"_L1, - "intro_start_position"_L1, - "intro_end_position"_L1, - "outro_start_position"_L1, - "outro_end_position"_L1}; + "intro_start_position"_L1, + "intro_end_position"_L1, + "outro_start_position"_L1, + "outro_end_position"_L1}; return std::any_of(list.cbegin(), list.cend(), [positionControl](auto& view) { return view == positionControl; }); From 26c80c1cf2ed73b47ee50934dd65b1c5588c65fb Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 4 May 2024 03:13:30 +0200 Subject: [PATCH 20/30] deal with markers that are not on the beat --- .../renderers/allshader/waveformrendermark.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 3846bf15ef1..f68571f5145 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -493,6 +493,16 @@ void allshader::WaveformRenderMark::updateUntilMark( auto itB = trackBeats->iteratorFrom( mixxx::audio::FramePos::fromEngineSamplePos(nextMarkPosition)); + // itB is the beat at or after the nextMarkPosition. + if (itB->toEngineSamplePos() > nextMarkPosition) { + // if itB is after nextMarkPosition, the previous beat might be closer + // and it the one we are interested in + if (nextMarkPosition - (itB - 1)->toEngineSamplePos() < + itB->toEngineSamplePos() - nextMarkPosition) { + itB--; + } + } + if (std::abs(itA->toEngineSamplePos() - playPosition) < 1) { m_currentBeatPosition = itA->toEngineSamplePos(); m_beatsUntilMark = std::distance(itA, itB); From 0346eb61eb9b72bb83ef62306bf38d4e7a4aa09e Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 4 May 2024 03:26:51 +0200 Subject: [PATCH 21/30] Qt::Literals::StringLiterals available since Qt 6.4 --- src/waveform/renderers/waveformmark.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index dac6ca5b98e..bcd3c127897 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -67,15 +67,25 @@ float overlappingMarkerIncrement(const float labelRectHeight, const float breadt return std::max(minIncrement, fullIncrement - std::max(0.f, threshold - breadth)); } +#define FOO + bool isShowUntilNextPositionControl(const QString& positionControl) { // To identify which markers are included in the beat/time until next marker // display, in addition to the hotcues +#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0) using namespace Qt::Literals::StringLiterals; constexpr std::array list = {"cue_point"_L1, "intro_start_position"_L1, "intro_end_position"_L1, "outro_start_position"_L1, "outro_end_position"_L1}; +#else + constexpr std::array list = {QLatin1String{"cue_point"}, + QLatin1String{"intro_start_position"}, + QLatin1String{"intro_end_position"}, + QLatin1String{"outro_start_position"}, + QLatin1String{"outro_end_position"}}; +#endif return std::any_of(list.cbegin(), list.cend(), [positionControl](auto& view) { return view == positionControl; }); From b426beae02557eda4addc944c2ebe43bec9078b4 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sat, 4 May 2024 14:34:37 +0200 Subject: [PATCH 22/30] fix clazy --- src/waveform/renderers/waveformmark.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index bcd3c127897..33b0d95bc16 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -80,7 +80,7 @@ bool isShowUntilNextPositionControl(const QString& positionControl) { "outro_start_position"_L1, "outro_end_position"_L1}; #else - constexpr std::array list = {QLatin1String{"cue_point"}, + const std::array list = {QLatin1String{"cue_point"}, QLatin1String{"intro_start_position"}, QLatin1String{"intro_end_position"}, QLatin1String{"outro_start_position"}, From 3429891bb3a517d6ecd123b117f24748b96d3a81 Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 5 May 2024 01:37:33 +0200 Subject: [PATCH 23/30] prefs layout tweak --- src/preferences/dialog/dlgprefwaveform.cpp | 2 + src/preferences/dialog/dlgprefwaveformdlg.ui | 134 +++++++------------ 2 files changed, 51 insertions(+), 85 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 70b30d05e9b..13530958eb4 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -290,7 +290,9 @@ void DlgPrefWaveform::updateEnableUntilMark() { const bool enabled = WaveformWidgetFactory::instance()->widgetTypeSupportsUntilMark(); untilMarkShowBeatsCheckBox->setEnabled(enabled); untilMarkShowTimeCheckBox->setEnabled(enabled); + untilMarkAlignLabel->setEnabled(enabled); untilMarkAlignComboBox->setEnabled(enabled); + untilMarkTextPixelSizeLabel->setEnabled(enabled); untilMarkTextPixelSizeSpinBox->setEnabled(enabled); requiresGLSLLabel->setVisible(!enabled); } diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index c2babe1a8e4..0ddfc878729 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -468,103 +468,67 @@ Select from different types of displays for the waveform, which differ primarily - + Beats until next marker - + Time until next marker - - - - - - Placement - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - untilMarkAlignComboBox - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - 20 - 20 - - - - - - - - Font size - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - untilMarkTextPixelSizeSpinBox - - - - - - - - - - px - - - 20 - - - 40 - - - 30 - - - - - - - Qt::Horizontal - - - - 1 - 0 - - - - - + + + + Placement + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilMarkAlignComboBox + + + + + + + + + + Font size + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + untilMarkTextPixelSizeSpinBox + + + + + + + + + + px + + + 20 + + + 40 + + + 30 + + From 0a798950df3b5099abe66e734421e9ad62fa28a7 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 5 May 2024 15:40:51 +0200 Subject: [PATCH 24/30] use constexpr and debug_assert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Schürmann --- src/waveform/renderers/allshader/digitsrenderer.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 6c44f6d6067..f029e932421 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -24,13 +24,13 @@ constexpr qreal SPACE = 3; // extra kerning when rendering chars constexpr qreal KERNING = 1; -char indexToChar(int index) { - const char* str = "0123456789:."; +constexpr char indexToChar(int index) { + constexpr char str[] = "0123456789:."; return str[index]; } -int charToIndex(QChar ch) { - int value = ch.digitValue(); - if (value != -1) { +constexpr int charToIndex(QChar ch) { + int value = ch.toLatin1() - '0'; + if (value >= 0 && value <= 9) { return value; } if (ch == ':') { @@ -39,7 +39,7 @@ int charToIndex(QChar ch) { if (ch == '.') { return 11; } - assert(false); + DEBUG_ASSERT(false); return 11; // fallback to dot } } // namespace From d320fa56ac4059c6583dcae5442b6ab1018bc70b Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 5 May 2024 18:01:29 +0200 Subject: [PATCH 25/30] use pointsize instead of pixelsize, keep text below third of waveform viewer height --- src/preferences/dialog/dlgprefwaveform.cpp | 14 ++-- src/preferences/dialog/dlgprefwaveform.h | 2 +- src/preferences/dialog/dlgprefwaveformdlg.ui | 12 ++-- .../renderers/allshader/digitsrenderer.cpp | 72 ++++++++++++++----- .../renderers/allshader/digitsrenderer.h | 9 +-- .../allshader/waveformrendermark.cpp | 42 ++++++----- .../renderers/allshader/waveformrendermark.h | 2 +- src/waveform/waveformwidgetfactory.cpp | 16 ++--- src/waveform/waveformwidgetfactory.h | 8 +-- 9 files changed, 109 insertions(+), 68 deletions(-) diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 13530958eb4..15e0daf6c0b 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -148,10 +148,10 @@ DlgPrefWaveform::DlgPrefWaveform( QOverload::of(&QComboBox::currentIndexChanged), this, &DlgPrefWaveform::slotSetUntilMarkAlign); - connect(untilMarkTextPixelSizeSpinBox, + connect(untilMarkTextPointSizeSpinBox, QOverload::of(&QSpinBox::valueChanged), this, - &DlgPrefWaveform::slotSetUntilMarkTextPixelSize); + &DlgPrefWaveform::slotSetUntilMarkTextPointSize); setScrollSafeGuardForAllInputWidgets(this); } @@ -197,7 +197,7 @@ void DlgPrefWaveform::slotUpdate() { untilMarkAlignComboBox->setCurrentIndex( WaveformWidgetFactory::toUntilMarkAlignIndex( factory->getUntilMarkAlign())); - untilMarkTextPixelSizeSpinBox->setValue(factory->getUntilMarkTextPixelSize()); + untilMarkTextPointSizeSpinBox->setValue(factory->getUntilMarkTextPointSize()); // By default we set RGB woverview = "2" int overviewType = m_pConfig->getValue( @@ -292,8 +292,8 @@ void DlgPrefWaveform::updateEnableUntilMark() { untilMarkShowTimeCheckBox->setEnabled(enabled); untilMarkAlignLabel->setEnabled(enabled); untilMarkAlignComboBox->setEnabled(enabled); - untilMarkTextPixelSizeLabel->setEnabled(enabled); - untilMarkTextPixelSizeSpinBox->setEnabled(enabled); + untilMarkTextPointSizeLabel->setEnabled(enabled); + untilMarkTextPointSizeSpinBox->setEnabled(enabled); requiresGLSLLabel->setVisible(!enabled); } @@ -368,8 +368,8 @@ void DlgPrefWaveform::slotSetUntilMarkAlign(int index) { WaveformWidgetFactory::toUntilMarkAlign(index)); } -void DlgPrefWaveform::slotSetUntilMarkTextPixelSize(int value) { - WaveformWidgetFactory::instance()->setUntilMarkTextPixelSize(value); +void DlgPrefWaveform::slotSetUntilMarkTextPointSize(int value) { + WaveformWidgetFactory::instance()->setUntilMarkTextPointSize(value); } void DlgPrefWaveform::calculateCachedWaveformDiskUsage() { diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index fcf2e13817b..e141c17f996 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -41,7 +41,7 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotSetUntilMarkShowBeats(bool checked); void slotSetUntilMarkShowTime(bool checked); void slotSetUntilMarkAlign(int index); - void slotSetUntilMarkTextPixelSize(int value); + void slotSetUntilMarkTextPointSize(int value); signals: void reloadUserInterface(); diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 0ddfc878729..6c42b4bf020 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -499,7 +499,7 @@ Select from different types of displays for the waveform, which differ primarily - + Font size @@ -507,23 +507,23 @@ Select from different types of displays for the waveform, which differ primarily Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - untilMarkTextPixelSizeSpinBox + untilMarkTextPointSizeSpinBox - + - px + pt - 20 + 10 - 40 + 50 30 diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index f029e932421..674cfeaf110 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -9,6 +9,7 @@ #include #include +#include "./util/assert.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/allshader/vertexdata.h" @@ -52,30 +53,64 @@ void allshader::DigitsRenderer::init() { } float allshader::DigitsRenderer::height() const { - return static_cast(m_texture.height()); + return m_height; } -void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float devicePixelRatio) { - QFont font; - font.setFamily("Open Sans"); - font.setPixelSize(fontPixelSize); +void allshader::DigitsRenderer::updateTexture( + float fontPointSize, float maxHeight, float devicePixelRatio) { + // Check if we need to update the texture: when the requested font size + // changes, or when the max height is too small for the current font size or + // different from the max height we previously adjusted the font size for. + if (fontPointSize == m_fontPointSize && maxHeight >= m_height && + (m_adjustedForMaxHeight == 0.f || + maxHeight == m_adjustedForMaxHeight)) { + return; + } - QFontMetricsF metrics{font}; + m_fontPointSize = fontPointSize; + m_adjustedForMaxHeight = 0.f; - qreal totalTextWidth = 0; - qreal maxTextHeight = 0; - for (int i = 0; i < NUM_CHARS; i++) { - assert(charToIndex(indexToChar(i)) == i); - const QString text(indexToChar(i)); - const auto rect = metrics.tightBoundingRect(text); - maxTextHeight = std::max(maxTextHeight, rect.height()); - totalTextWidth += metrics.horizontalAdvance(text) + SPACE + SPACE; - } + QFont font; + QFontMetricsF metrics{font}; + font.setFamily("Open Sans"); + qreal totalTextWidth; + qreal maxTextHeight; + qreal heightWithPadding; + + bool retry = false; + do { + font.setPointSizeF(fontPointSize); + + metrics = QFontMetricsF{font}; + + totalTextWidth = 0; + maxTextHeight = 0; + + for (int i = 0; i < NUM_CHARS; i++) { + assert(charToIndex(indexToChar(i)) == i); + const QString text(indexToChar(i)); + const auto rect = metrics.tightBoundingRect(text); + maxTextHeight = std::max(maxTextHeight, rect.height()); + totalTextWidth += metrics.horizontalAdvance(text) + SPACE + SPACE; + } + heightWithPadding = std::ceil(maxTextHeight + SPACE + SPACE); + qDebug() << "XXXXXX" << retry << maxHeight << maxTextHeight + << heightWithPadding << fontPointSize; + if (!retry && heightWithPadding > maxHeight) { + // We need to adjust the font size. Only do this once. + fontPointSize *= maxHeight / static_cast(heightWithPadding); + m_adjustedForMaxHeight = maxHeight; + retry = true; + } else { + retry = false; + } + } while (retry); + m_height = std::min(static_cast(heightWithPadding), maxHeight); totalTextWidth = std::ceil(totalTextWidth); QImage image(static_cast(totalTextWidth * devicePixelRatio), - static_cast(std::ceil(maxTextHeight + SPACE + SPACE) * devicePixelRatio), + static_cast(m_height * devicePixelRatio), QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(devicePixelRatio); image.fill(Qt::transparent); @@ -139,8 +174,7 @@ void allshader::DigitsRenderer::generateTexture(int fontPixelSize, float deviceP float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, float x, float y, - const QString& s, - float devicePixelRatio) { + const QString& s) { const int n = s.length(); const float x0 = x; const float dx = static_cast(-(SPACE + SPACE) + KERNING); @@ -158,7 +192,7 @@ float allshader::DigitsRenderer::draw(const QMatrix4x4& matrix, posVertices.addRectangle(x, y, x + m_width[index], - y + height() / devicePixelRatio); + y + height()); x += m_width[index] + dx; } diff --git a/src/waveform/renderers/allshader/digitsrenderer.h b/src/waveform/renderers/allshader/digitsrenderer.h index 325c79070b4..1e352ae9942 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.h +++ b/src/waveform/renderers/allshader/digitsrenderer.h @@ -15,12 +15,11 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { ~DigitsRenderer(); void init(); - void generateTexture(int fontPixelSize, float devicePixelRatio); + void updateTexture(float fontPointSize, float maxHeight, float devicePixelRatio); float draw(const QMatrix4x4& matrix, float x, float y, - const QString& s, - float devicePixelRatio); + const QString& s); float height() const; private: @@ -28,6 +27,8 @@ class allshader::DigitsRenderer : public QOpenGLFunctions { OpenGLTexture2D m_texture; float m_offset[13]; float m_width[12]; - + float m_fontPointSize{}; + float m_height{}; + float m_adjustedForMaxHeight{}; DISALLOW_COPY_AND_ASSIGN(DigitsRenderer); }; diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index f68571f5145..ade344efcf3 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -82,9 +82,11 @@ void allshader::WaveformRenderMark::initializeGL() { // Will create textures so requires OpenGL context updateMarkImages(); updatePlayPosMarkTexture(); - m_untilMarkTextPixelSize = WaveformWidgetFactory::instance()->getUntilMarkTextPixelSize(); - m_digitsRenderer.generateTexture( - m_untilMarkTextPixelSize, m_waveformRenderer->getDevicePixelRatio()); + const auto untilMarkTextPointSize = + WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); + m_digitsRenderer.updateTexture(untilMarkTextPointSize, + getMaxHeightForText(), + m_waveformRenderer->getDevicePixelRatio()); } void allshader::WaveformRenderMark::drawTexture( @@ -325,25 +327,25 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa const auto untilMarkAlign = WaveformWidgetFactory::instance()->getUntilMarkAlign(); const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); - const int untilMarkTextPixelSize = - WaveformWidgetFactory::instance()->getUntilMarkTextPixelSize(); - if (untilMarkTextPixelSize != m_untilMarkTextPixelSize) { - m_untilMarkTextPixelSize = untilMarkTextPixelSize; - m_digitsRenderer.generateTexture(m_untilMarkTextPixelSize, - m_waveformRenderer->getDevicePixelRatio()); - } + const auto untilMarkTextPointSize = + WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); + m_digitsRenderer.updateTexture(untilMarkTextPointSize, + getMaxHeightForText(), + m_waveformRenderer->getDevicePixelRatio()); if (m_timeUntilMark == 0.0) { return; } - const float ch = m_digitsRenderer.height() / devicePixelRatio; + const float ch = m_digitsRenderer.height(); float y = untilMarkAlign == Qt::AlignTop ? 0.f : untilMarkAlign == Qt::AlignBottom ? m_waveformRenderer->getBreadth() - ch : m_waveformRenderer->getBreadth() / 2.f; - if (untilMarkShowBeats && untilMarkShowTime) { + bool multiLine = untilMarkShowBeats && untilMarkShowTime && ch * 2.f < getMaxHeightForText(); + + if (multiLine) { if (untilMarkAlign != Qt::AlignTop) { y -= ch; } @@ -355,13 +357,14 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa } if (untilMarkShowBeats) { - m_digitsRenderer.draw(matrix, + const auto w = m_digitsRenderer.draw(matrix, x, y, - QString::number(m_beatsUntilMark), - devicePixelRatio); - if (untilMarkShowTime) { + QString::number(m_beatsUntilMark)); + if (multiLine) { y += ch; + } else { + x += w + ch * 0.75f; } } @@ -369,8 +372,7 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa m_digitsRenderer.draw(matrix, x, y, - timeSecToString(m_timeUntilMark), - devicePixelRatio); + timeSecToString(m_timeUntilMark)); } } @@ -520,3 +522,7 @@ void allshader::WaveformRenderMark::updateUntilMark( remainingTime * (nextMarkPosition - playPosition) / (endPosition - playPosition)); } + +float allshader::WaveformRenderMark::getMaxHeightForText() const { + return m_waveformRenderer->getBreadth() / 3; +} diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index a7b3a31a54d..317f132e26d 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -54,6 +54,7 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, void drawTexture(const QMatrix4x4& matrix, float x, float y, QOpenGLTexture* texture); void updateUntilMark(double playPosition, double markerPosition); void drawUntilMark(const QMatrix4x4& matrix, float x); + float getMaxHeightForText() const; mixxx::RGBAShader m_rgbaShader; mixxx::TextureShader m_textureShader; @@ -63,7 +64,6 @@ class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, double m_timeUntilMark; double m_currentBeatPosition; double m_nextBeatPosition; - int m_untilMarkTextPixelSize; std::unique_ptr m_pTimeRemainingControl; bool m_isSlipRenderer; diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 082ddec19fe..a01452b8fdb 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -114,7 +114,7 @@ WaveformWidgetFactory::WaveformWidgetFactory() m_untilMarkShowBeats(false), m_untilMarkShowTime(false), m_untilMarkAlign(Qt::AlignVCenter), - m_untilMarkTextPixelSize(24), + m_untilMarkTextPointSize(24), m_openGlAvailable(false), m_openGlesAvailable(false), m_openGLShaderAvailable(false), @@ -431,9 +431,9 @@ bool WaveformWidgetFactory::setConfig(UserSettingsPointer config) { setUntilMarkAlign(toUntilMarkAlign( m_config->getValue(ConfigKey("[Waveform]", "UntilMarkAlign"), toUntilMarkAlignIndex(m_untilMarkAlign)))); - setUntilMarkTextPixelSize( - m_config->getValue(ConfigKey("[Waveform]", "UntilMarkTextPixelSize"), - m_untilMarkTextPixelSize)); + setUntilMarkTextPointSize( + m_config->getValue(ConfigKey("[Waveform]", "UntilMarkTextPointSize"), + m_untilMarkTextPointSize)); return true; } @@ -1322,11 +1322,11 @@ void WaveformWidgetFactory::setUntilMarkAlign(Qt::Alignment align) { toUntilMarkAlignIndex(m_untilMarkAlign)); } } -void WaveformWidgetFactory::setUntilMarkTextPixelSize(int value) { - m_untilMarkTextPixelSize = value; +void WaveformWidgetFactory::setUntilMarkTextPointSize(int value) { + m_untilMarkTextPointSize = value; if (m_config) { - m_config->setValue(ConfigKey("[Waveform]", "UntilMarkTextPixelSize"), - m_untilMarkTextPixelSize); + m_config->setValue(ConfigKey("[Waveform]", "UntilMarkTextPointSize"), + m_untilMarkTextPointSize); } } diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index cd260871f3f..1c00cff50b6 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -102,7 +102,7 @@ class WaveformWidgetFactory : public QObject, public Singleton Date: Sun, 5 May 2024 18:04:21 +0200 Subject: [PATCH 26/30] remove unused var --- src/waveform/renderers/allshader/waveformrendermark.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index ade344efcf3..64b13afe59c 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -325,7 +325,6 @@ void allshader::WaveformRenderMark::drawUntilMark(const QMatrix4x4& matrix, floa const bool untilMarkShowBeats = WaveformWidgetFactory::instance()->getUntilMarkShowBeats(); const bool untilMarkShowTime = WaveformWidgetFactory::instance()->getUntilMarkShowTime(); const auto untilMarkAlign = WaveformWidgetFactory::instance()->getUntilMarkAlign(); - const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); const auto untilMarkTextPointSize = WaveformWidgetFactory::instance()->getUntilMarkTextPointSize(); From 884cad8c51cee61806865cefdedbc88b300a500a Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 5 May 2024 18:05:52 +0200 Subject: [PATCH 27/30] remove debug output --- src/waveform/renderers/allshader/digitsrenderer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 674cfeaf110..e4680733ca6 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -94,8 +94,6 @@ void allshader::DigitsRenderer::updateTexture( totalTextWidth += metrics.horizontalAdvance(text) + SPACE + SPACE; } heightWithPadding = std::ceil(maxTextHeight + SPACE + SPACE); - qDebug() << "XXXXXX" << retry << maxHeight << maxTextHeight - << heightWithPadding << fontPointSize; if (!retry && heightWithPadding > maxHeight) { // We need to adjust the font size. Only do this once. fontPointSize *= maxHeight / static_cast(heightWithPadding); From 18ebb1c2412ae3e5053de18f0071bc30bfc3d62a Mon Sep 17 00:00:00 2001 From: m0dB Date: Sun, 5 May 2024 23:11:35 +0200 Subject: [PATCH 28/30] formatting --- src/waveform/renderers/allshader/digitsrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index e4680733ca6..17ec0994800 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -26,7 +26,7 @@ constexpr qreal SPACE = 3; constexpr qreal KERNING = 1; constexpr char indexToChar(int index) { - constexpr char str[] = "0123456789:."; + constexpr char str[] = "0123456789:."; return str[index]; } constexpr int charToIndex(QChar ch) { From feaa340e17225c7d852b99721cffa29d5cb1759d Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 6 May 2024 19:55:14 +0200 Subject: [PATCH 29/30] added static_assert as suggested by daschuer --- src/waveform/renderers/allshader/digitsrenderer.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/waveform/renderers/allshader/digitsrenderer.cpp b/src/waveform/renderers/allshader/digitsrenderer.cpp index 17ec0994800..ed939446b2a 100644 --- a/src/waveform/renderers/allshader/digitsrenderer.cpp +++ b/src/waveform/renderers/allshader/digitsrenderer.cpp @@ -43,6 +43,16 @@ constexpr int charToIndex(QChar ch) { DEBUG_ASSERT(false); return 11; // fallback to dot } +constexpr bool checkCharToIndex() { + for (int i = 0; i < NUM_CHARS; i++) { + if (charToIndex(indexToChar(i)) != i) { + return false; + } + } + return true; +} +static_assert(checkCharToIndex()); + } // namespace allshader::DigitsRenderer::~DigitsRenderer() = default; @@ -87,7 +97,6 @@ void allshader::DigitsRenderer::updateTexture( maxTextHeight = 0; for (int i = 0; i < NUM_CHARS; i++) { - assert(charToIndex(indexToChar(i)) == i); const QString text(indexToChar(i)); const auto rect = metrics.tightBoundingRect(text); maxTextHeight = std::max(maxTextHeight, rect.height()); From 362ace22cbb9ed2679ac79cdb94465061f0ac0c0 Mon Sep 17 00:00:00 2001 From: m0dB Date: Mon, 6 May 2024 19:48:19 +0200 Subject: [PATCH 30/30] post-rebase fixes post-rebase fixes post-rebase fixes post-rebase-fixes --- .../allshader/waveformrendermark.cpp | 103 ++++++++---------- .../renderers/waveformwidgetrenderer.h | 5 +- 2 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 64b13afe59c..a6468920071 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -59,7 +59,8 @@ QString timeSecToString(double timeSec) { } // namespace allshader::WaveformRenderMark::WaveformRenderMark( - WaveformWidgetRenderer* waveformWidget) + WaveformWidgetRenderer* waveformWidget, + ::WaveformRendererAbstract::PositionSource type) : ::WaveformRenderMarkBase(waveformWidget, false), m_beatsUntilMark(0), m_timeUntilMark(0.0), @@ -197,11 +198,11 @@ void allshader::WaveformRenderMark::paintGL() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - // Will create textures so requires OpenGL context for (const auto& pMark : std::as_const(m_marks)) { pMark->setBreadth(slipActive ? m_waveformRenderer->getBreadth() / 2 : m_waveformRenderer->getBreadth()); } + // Will create textures so requires OpenGL context updateMarkImages(); QMatrix4x4 matrix = matrixForWidgetGeometry(m_waveformRenderer, false); @@ -239,64 +240,54 @@ void allshader::WaveformRenderMark::paintGL() { } const double sampleEndPosition = pMark->getSampleEndPosition(); - if (samplePosition != Cue::kNoPosition) { - const float currentMarkPoint = - std::round( - static_cast( - m_waveformRenderer - ->transformSamplePositionInRendererWorld( - samplePosition, positionType)) * - devicePixelRatio) / - devicePixelRatio; - if (pMark->isShowUntilNext() && - samplePosition >= playPosition + 1.0 && - samplePosition < nextMarkPosition) { - nextMarkPosition = samplePosition; - } - const double sampleEndPosition = pMark->getSampleEndPosition(); - - bool visible = false; - // Check if the current point needs to be displayed. - if (drawOffset > -markHalfWidth && - drawOffset < m_waveformRenderer->getLength() + - markHalfWidth) { - drawTexture(drawOffset, - !m_isSlipRenderer && slipActive - ? m_waveformRenderer->getBreadth() / 2 - : 0, - pTexture); - visible = true; - } + // Pixmaps are expected to have the mark stroke at the center, + // and preferably have an odd width in order to have the stroke + // exactly at the sample position. + const float markHalfWidth = pTexture->width() / devicePixelRatio / 2.f; + const float drawOffset = currentMarkPoint - markHalfWidth; - // Check if the range needs to be displayed. - if (samplePosition != sampleEndPosition && sampleEndPosition != Cue::kNoPosition) { - DEBUG_ASSERT(samplePosition < sampleEndPosition); - const float currentMarkEndPoint = static_cast< - float>( - m_waveformRenderer - ->transformSamplePositionInRendererWorld( - sampleEndPosition, positionType)); - - if (visible || currentMarkEndPoint > 0) { - QColor color = pMark->fillColor(); - color.setAlphaF(0.4f); - - drawMark(matrix, - QRectF(QPointF(currentMarkPoint, 0), - QPointF(currentMarkEndPoint, - m_waveformRenderer - ->getBreadth())), - color); - visible = true; - } - } + bool visible = false; + // Check if the current point needs to be displayed. + if (drawOffset > -markHalfWidth && + drawOffset < m_waveformRenderer->getLength() + + markHalfWidth) { + drawTexture(matrix, + drawOffset, + !m_isSlipRenderer && slipActive + ? m_waveformRenderer->getBreadth() / 2 + : 0, + pTexture); + visible = true; + } - if (visible) { - marksOnScreen.append( - WaveformWidgetRenderer::WaveformMarkOnScreen{ - pMark, static_cast(drawOffset)}); + // Check if the range needs to be displayed. + if (samplePosition != sampleEndPosition && sampleEndPosition != Cue::kNoPosition) { + DEBUG_ASSERT(samplePosition < sampleEndPosition); + const float currentMarkEndPoint = static_cast< + float>( + m_waveformRenderer + ->transformSamplePositionInRendererWorld( + sampleEndPosition, positionType)); + + if (visible || currentMarkEndPoint > 0) { + QColor color = pMark->fillColor(); + color.setAlphaF(0.4f); + + drawMark(matrix, + QRectF(QPointF(currentMarkPoint, 0), + QPointF(currentMarkEndPoint, + m_waveformRenderer + ->getBreadth())), + color); + visible = true; } } + + if (visible) { + marksOnScreen.append( + WaveformWidgetRenderer::WaveformMarkOnScreen{ + pMark, static_cast(drawOffset)}); + } } m_waveformRenderer->setMarkPositions(marksOnScreen); diff --git a/src/waveform/renderers/waveformwidgetrenderer.h b/src/waveform/renderers/waveformwidgetrenderer.h index 5d724da7e9d..47cd21b67a6 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.h +++ b/src/waveform/renderers/waveformwidgetrenderer.h @@ -68,8 +68,9 @@ class WaveformWidgetRenderer { return m_lastDisplayedPosition[type]; } - double getTruePosSample() const { - return m_truePosSample; + double getTruePosSample(::WaveformRendererAbstract::PositionSource type = + ::WaveformRendererAbstract::Play) const { + return m_truePosSample[type]; } void setZoom(double zoom);