diff options
author | m0dB <79429057+m0dB@users.noreply.github.com> | 2023-04-15 18:45:41 +0200 |
---|---|---|
committer | m0dB <79429057+m0dB@users.noreply.github.com> | 2023-05-27 01:28:24 +0200 |
commit | fafa14f4619036ab95ff701df5036566790c0489 (patch) | |
tree | f5363092bf144f7b0b78fee74d648e77a5dddf79 | |
parent | 4f6a056e331bb421de09cd0a5480237285872aa7 (diff) |
added simple qopengl waveform renderer
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/waveform/renderers/qopengl/waveformrendererfiltered.cpp | 23 | ||||
-rw-r--r-- | src/waveform/renderers/qopengl/waveformrendererfiltered.h | 2 | ||||
-rw-r--r-- | src/waveform/renderers/qopengl/waveformrendererlrrgb.cpp | 5 | ||||
-rw-r--r-- | src/waveform/renderers/qopengl/waveformrendererrgb.cpp | 5 | ||||
-rw-r--r-- | src/waveform/renderers/qopengl/waveformrenderersimple.cpp | 181 | ||||
-rw-r--r-- | src/waveform/renderers/qopengl/waveformrenderersimple.h | 28 | ||||
-rw-r--r-- | src/waveform/waveformwidgetfactory.cpp | 11 | ||||
-rw-r--r-- | src/waveform/widgets/qopengl/simplewaveformwidget.cpp | 36 | ||||
-rw-r--r-- | src/waveform/widgets/qopengl/simplewaveformwidget.h | 46 | ||||
-rw-r--r-- | src/waveform/widgets/waveformwidgettype.h | 1 |
11 files changed, 328 insertions, 14 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 74dfbc55ca..13811ec83a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1182,13 +1182,13 @@ else() ) if(QOPENGL) target_sources(mixxx-lib PRIVATE + src/waveform/renderers/qopengl/calculatematrix.cpp src/waveform/renderers/qopengl/shaders/colorshader.cpp src/waveform/renderers/qopengl/shaders/endoftrackshader.cpp src/waveform/renderers/qopengl/shaders/gradientshader.cpp src/waveform/renderers/qopengl/shaders/shader.cpp src/waveform/renderers/qopengl/shaders/textureshader.cpp src/waveform/renderers/qopengl/shaders/unicolorshader.cpp - src/waveform/renderers/qopengl/calculatematrix.cpp src/waveform/renderers/qopengl/waveformrenderbackground.cpp src/waveform/renderers/qopengl/waveformrenderbeat.cpp src/waveform/renderers/qopengl/waveformrenderer.cpp @@ -1198,11 +1198,13 @@ else() src/waveform/renderers/qopengl/waveformrendererpreroll.cpp src/waveform/renderers/qopengl/waveformrendererrgb.cpp src/waveform/renderers/qopengl/waveformrenderersignalbase.cpp + src/waveform/renderers/qopengl/waveformrenderersimple.cpp src/waveform/renderers/qopengl/waveformrendermark.cpp src/waveform/renderers/qopengl/waveformrendermarkrange.cpp src/waveform/widgets/qopengl/filteredwaveformwidget.cpp src/waveform/widgets/qopengl/lrrgbwaveformwidget.cpp src/waveform/widgets/qopengl/rgbwaveformwidget.cpp + src/waveform/widgets/qopengl/simplewaveformwidget.cpp src/waveform/widgets/qopengl/waveformwidget.cpp src/widget/openglwindow.cpp src/widget/qopengl/wspinny.cpp diff --git a/src/waveform/renderers/qopengl/waveformrendererfiltered.cpp b/src/waveform/renderers/qopengl/waveformrendererfiltered.cpp index f411026688..42458d179e 100644 --- a/src/waveform/renderers/qopengl/waveformrendererfiltered.cpp +++ b/src/waveform/renderers/qopengl/waveformrendererfiltered.cpp @@ -81,17 +81,17 @@ void WaveformRendererFiltered::renderGL() { int reserved[4]; // low, mid, high for (int bandIndex = 0; bandIndex < 3; bandIndex++) { - m_verticesForGroup[bandIndex].clear(); + m_vertices[bandIndex].clear(); reserved[bandIndex] = numVerticesPerLine * length; - m_verticesForGroup[bandIndex].reserve(reserved[bandIndex]); + m_vertices[bandIndex].reserve(reserved[bandIndex]); } // the horizontal line reserved[3] = numVerticesPerLine; - m_verticesForGroup[3].clear(); - m_verticesForGroup[3].reserve(reserved[3]); + m_vertices[3].clear(); + m_vertices[3].reserve(reserved[3]); - m_verticesForGroup[3].addRectangle( + m_vertices[3].addRectangle( 0.f, halfBreadth - 0.5f * devicePixelRatio, static_cast<float>(length), @@ -148,7 +148,7 @@ void WaveformRendererFiltered::renderGL() { max[bandIndex][1] *= bandGain[bandIndex]; // lines are thin rectangles - m_verticesForGroup[bandIndex].addRectangle( + m_vertices[bandIndex].addRectangle( fpos - 0.5f, halfBreadth - heightFactor * max[bandIndex][0], fpos + 0.5f, @@ -179,17 +179,20 @@ void WaveformRendererFiltered::renderGL() { colors[2].setRgbF(static_cast<float>(m_rgbHighColor_r), static_cast<float>(m_rgbHighColor_g), static_cast<float>(m_rgbHighColor_b)); - colors[3].setRgbF(1.f, 1.f, 1.f); + colors[4].setRgbF(static_cast<float>(m_axesColor_r), + static_cast<float>(m_axesColor_g), + static_cast<float>(m_axesColor_b), + static_cast<float>(m_axesColor_a)); // 3 bands + 1 extra for the horizontal line for (int i = 0; i < 4; i++) { - DEBUG_ASSERT(reserved[i] == m_verticesForGroup[i].size()); + DEBUG_ASSERT(reserved[i] == m_vertices[i].size()); m_shader.setUniformValue(colorLocation, colors[i]); m_shader.setAttributeArray( - positionLocation, GL_FLOAT, m_verticesForGroup[i].constData(), 2); + positionLocation, GL_FLOAT, m_vertices[i].constData(), 2); - glDrawArrays(GL_TRIANGLES, 0, m_verticesForGroup[i].size()); + glDrawArrays(GL_TRIANGLES, 0, m_vertices[i].size()); } m_shader.disableAttributeArray(positionLocation); diff --git a/src/waveform/renderers/qopengl/waveformrendererfiltered.h b/src/waveform/renderers/qopengl/waveformrendererfiltered.h index 74fc5c211f..6b8c8b6690 100644 --- a/src/waveform/renderers/qopengl/waveformrendererfiltered.h +++ b/src/waveform/renderers/qopengl/waveformrendererfiltered.h @@ -22,7 +22,7 @@ class qopengl::WaveformRendererFiltered final : public qopengl::WaveformRenderer private: UnicolorShader m_shader; - VertexData m_verticesForGroup[4]; + VertexData m_vertices[4]; DISALLOW_COPY_AND_ASSIGN(WaveformRendererFiltered); }; diff --git a/src/waveform/renderers/qopengl/waveformrendererlrrgb.cpp b/src/waveform/renderers/qopengl/waveformrendererlrrgb.cpp index 465432ce88..24ac9fd19b 100644 --- a/src/waveform/renderers/qopengl/waveformrendererlrrgb.cpp +++ b/src/waveform/renderers/qopengl/waveformrendererlrrgb.cpp @@ -106,7 +106,10 @@ void WaveformRendererLRRGB::renderGL() { halfBreadth - 0.5f * devicePixelRatio, static_cast<float>(length), halfBreadth + 0.5f * devicePixelRatio); - m_colors.addForRectangle(1.f, 1.f, 1.f); + m_colors.addForRectangle( + static_cast<float>(m_axesColor_r), + static_cast<float>(m_axesColor_g), + static_cast<float>(m_axesColor_b)); for (int pos = 0; pos < length; ++pos) { // Our current pixel (x) corresponds to a number of visual samples diff --git a/src/waveform/renderers/qopengl/waveformrendererrgb.cpp b/src/waveform/renderers/qopengl/waveformrendererrgb.cpp index 9dc98f7477..2646bb106d 100644 --- a/src/waveform/renderers/qopengl/waveformrendererrgb.cpp +++ b/src/waveform/renderers/qopengl/waveformrendererrgb.cpp @@ -104,7 +104,10 @@ void WaveformRendererRGB::renderGL() { halfBreadth - 0.5f * devicePixelRatio, static_cast<float>(length), halfBreadth + 0.5f * devicePixelRatio); - m_colors.addForRectangle(1.f, 1.f, 1.f); + m_colors.addForRectangle( + static_cast<float>(m_axesColor_r), + static_cast<float>(m_axesColor_g), + static_cast<float>(m_axesColor_b)); for (int pos = 0; pos < length; ++pos) { // Our current pixel (x) corresponds to a number of visual samples diff --git a/src/waveform/renderers/qopengl/waveformrenderersimple.cpp b/src/waveform/renderers/qopengl/waveformrenderersimple.cpp new file mode 100644 index 0000000000..17ad3f440d --- /dev/null +++ b/src/waveform/renderers/qopengl/waveformrenderersimple.cpp @@ -0,0 +1,181 @@ +#include "waveform/renderers/qopengl/waveformrenderersimple.h" + +#include "track/track.h" +#include "util/math.h" +#include "waveform/renderers/qopengl/calculatematrix.h" +#include "waveform/waveform.h" +#include "waveform/waveformwidgetfactory.h" +#include "waveform/widgets/qopengl/waveformwidget.h" +#include "widget/wskincolor.h" +#include "widget/wwidget.h" + +using namespace qopengl; + +WaveformRendererSimple::WaveformRendererSimple( + WaveformWidgetRenderer* waveformWidget) + : WaveformRendererSignalBase(waveformWidget) { +} + +WaveformRendererSimple::~WaveformRendererSimple() { +} + +void WaveformRendererSimple::onSetup(const QDomNode& node) { + Q_UNUSED(node); +} + +void WaveformRendererSimple::initializeGL() { + m_shader.init(); +} + +void WaveformRendererSimple::renderGL() { + TrackPointer pTrack = m_waveformRenderer->getTrackInfo(); + if (!pTrack) { + return; + } + + ConstWaveformPointer waveform = pTrack->getWaveform(); + if (waveform.isNull()) { + return; + } + + const int dataSize = waveform->getDataSize(); + if (dataSize <= 1) { + return; + } + + const WaveformData* data = waveform->data(); + if (data == nullptr) { + return; + } + + const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); + const int length = static_cast<int>(m_waveformRenderer->getLength() * devicePixelRatio); + + // Not multiplying with devicePixelRatio will also work. In that case, on + // High-DPI-Display the lines will be devicePixelRatio pixels wide (which is + // also what is used for the beat grid and the markers), or in other words + // each block of samples is represented by devicePixelRatio pixels (width). + + const double firstVisualIndex = m_waveformRenderer->getFirstDisplayedPosition() * dataSize; + const double lastVisualIndex = m_waveformRenderer->getLastDisplayedPosition() * dataSize; + + // Represents the # of waveform data points per horizontal pixel. + const double visualIncrementPerPixel = + (lastVisualIndex - firstVisualIndex) / static_cast<double>(length); + + // Per-band gain from the EQ knobs. + float allGain{1.0}; + float bandGain[3] = {1.0, 1.0, 1.0}; + getGains(&allGain, &bandGain[0], &bandGain[1], &bandGain[2]); + + const float breadth = static_cast<float>(m_waveformRenderer->getBreadth()) * devicePixelRatio; + const float halfBreadth = breadth / 2.0f; + + const float heightFactor = allGain * halfBreadth / 255.f; + + // Effective visual index of x + double xVisualSampleIndex = firstVisualIndex; + + const int numVerticesPerLine = 6; // 2 triangles + + int reserved[2]; + + reserved[0] = numVerticesPerLine * length; + m_vertices[0].clear(); + m_vertices[0].reserve(reserved[0]); + + // the horizontal line + reserved[1] = numVerticesPerLine; + m_vertices[1].clear(); + m_vertices[1].reserve(reserved[1]); + + m_vertices[1].addRectangle( + 0.f, + halfBreadth - 0.5f * devicePixelRatio, + static_cast<float>(length), + halfBreadth + 0.5f * devicePixelRatio); + + for (int pos = 0; pos < length; ++pos) { + // Our current pixel (x) corresponds to a number of visual samples + // (visualSamplerPerPixel) in our waveform object. We take the max of + // all the data points on either side of xVisualSampleIndex within a + // window of 'maxSamplingRange' visual samples to measure the maximum + // data point contained by this pixel. + double maxSamplingRange = visualIncrementPerPixel / 2.0; + + // Since xVisualSampleIndex is in visual-samples (e.g. R,L,R,L) we want + // to check +/- maxSamplingRange frames, not samples. To do this, divide + // xVisualSampleIndex by 2. Since frames indices are integers, we round + // to the nearest integer by adding 0.5 before casting to int. + int visualFrameStart = int(xVisualSampleIndex / 2.0 - maxSamplingRange + 0.5); + int visualFrameStop = int(xVisualSampleIndex / 2.0 + maxSamplingRange + 0.5); + const int lastVisualFrame = dataSize / 2 - 1; + + // We now know that some subset of [visualFrameStart, visualFrameStop] + // lies within the valid range of visual frames. Clamp + // visualFrameStart/Stop to within [0, lastVisualFrame]. + visualFrameStart = math_clamp(visualFrameStart, 0, lastVisualFrame); + visualFrameStop = math_clamp(visualFrameStop, 0, lastVisualFrame); + + int visualIndexStart = visualFrameStart * 2; + int visualIndexStop = visualFrameStop * 2; + + visualIndexStart = std::max(visualIndexStart, 0); + visualIndexStop = std::min(visualIndexStop, dataSize); + + // 2 channels + float max[2]{}; + + for (int i = visualIndexStart; i < visualIndexStop; i += 2) { + for (int chn = 0; chn < 2; chn++) { + const WaveformData& waveformData = data[i + chn]; + const float filteredAll = static_cast<float>(waveformData.filtered.all); + + max[chn] = math_max(max[chn], filteredAll); + } + } + + const float fpos = static_cast<float>(pos); + + // lines are thin rectangles + m_vertices[0].addRectangle( + fpos - 0.5f, + halfBreadth - heightFactor * max[0], + fpos + 0.5f, + halfBreadth + heightFactor * max[1]); + + xVisualSampleIndex += visualIncrementPerPixel; + } + + const QMatrix4x4 matrix = calculateMatrix(m_waveformRenderer, true); + + const int matrixLocation = m_shader.uniformLocation("matrix"); + const int colorLocation = m_shader.uniformLocation("color"); + const int positionLocation = m_shader.attributeLocation("position"); + + m_shader.bind(); + m_shader.enableAttributeArray(positionLocation); + + m_shader.setUniformValue(matrixLocation, matrix); + + QColor colors[2]; + colors[0].setRgbF(static_cast<float>(m_signalColor_r), + static_cast<float>(m_signalColor_g), + static_cast<float>(m_signalColor_b)); + colors[1].setRgbF(static_cast<float>(m_axesColor_r), + static_cast<float>(m_axesColor_g), + static_cast<float>(m_axesColor_b), + static_cast<float>(m_axesColor_a)); + + for (int i = 0; i < 2; i++) { + DEBUG_ASSERT(reserved[i] == m_vertices[i].size()); + m_shader.setUniformValue(colorLocation, colors[i]); + m_shader.setAttributeArray( + positionLocation, GL_FLOAT, m_vertices[i].constData(), 2); + + glDrawArrays(GL_TRIANGLES, 0, m_vertices[i].size()); + } + + m_shader.disableAttributeArray(positionLocation); + m_shader.release(); +} diff --git a/src/waveform/renderers/qopengl/waveformrenderersimple.h b/src/waveform/renderers/qopengl/waveformrenderersimple.h new file mode 100644 index 0000000000..e63e7f6dba --- /dev/null +++ b/src/waveform/renderers/qopengl/waveformrenderersimple.h @@ -0,0 +1,28 @@ +#pragma once + +#include "util/class.h" +#include "waveform/renderers/qopengl/shaders/unicolorshader.h" +#include "waveform/renderers/qopengl/vertexdata.h" +#include "waveform/renderers/qopengl/waveformrenderersignalbase.h" + +namespace qopengl { +class WaveformRendererSimple; +} + +class qopengl::WaveformRendererSimple final : public qopengl::WaveformRendererSignalBase { + public: + explicit WaveformRendererSimple(WaveformWidgetRenderer* waveformWidget); + ~WaveformRendererSimple() override; + + // override ::WaveformRendererSignalBase + void onSetup(const QDomNode& node) override; + + void initializeGL() override; + void renderGL() override; + + private: + UnicolorShader m_shader; + VertexData m_vertices[2]; + + DISALLOW_COPY_AND_ASSIGN(WaveformRendererSimple); +}; diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 2ad7b2e15b..6ad520e86f 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -45,6 +45,7 @@ #include "waveform/widgets/qopengl/filteredwaveformwidget.h" #include "waveform/widgets/qopengl/lrrgbwaveformwidget.h" #include "waveform/widgets/qopengl/rgbwaveformwidget.h" +#include "waveform/widgets/qopengl/simplewaveformwidget.h" #endif #include "widget/wvumeter.h" #include "widget/wvumetergl.h" @@ -968,6 +969,13 @@ void WaveformWidgetFactory::evaluateWidgets() { useOpenGLShaders = qopengl::FilteredWaveformWidget::useOpenGLShaders(); developerOnly = qopengl::FilteredWaveformWidget::developerOnly(); break; + case WaveformWidgetType::QOpenGLSimpleWaveform: + widgetName = qopengl::SimpleWaveformWidget::getWaveformWidgetName(); + useOpenGl = qopengl::SimpleWaveformWidget::useOpenGl(); + useOpenGles = qopengl::SimpleWaveformWidget::useOpenGles(); + useOpenGLShaders = qopengl::SimpleWaveformWidget::useOpenGLShaders(); + developerOnly = qopengl::SimpleWaveformWidget::developerOnly(); + break; #endif default: DEBUG_ASSERT(!"Unexpected WaveformWidgetType"); @@ -1085,6 +1093,9 @@ WaveformWidgetAbstract* WaveformWidgetFactory::createWaveformWidget( case WaveformWidgetType::QOpenGLFilteredWaveform: widget = new qopengl::FilteredWaveformWidget(viewer->getGroup(), viewer); break; + case WaveformWidgetType::QOpenGLSimpleWaveform: + widget = new qopengl::SimpleWaveformWidget(viewer->getGroup(), viewer); + break; #endif default: //case WaveformWidgetType::SoftwareSimpleWaveform: TODO: (vrince) diff --git a/src/waveform/widgets/qopengl/simplewaveformwidget.cpp b/src/waveform/widgets/qopengl/simplewaveformwidget.cpp new file mode 100644 index 0000000000..6e2a98b511 --- /dev/null +++ b/src/waveform/widgets/qopengl/simplewaveformwidget.cpp @@ -0,0 +1,36 @@ +#include "waveform/widgets/qopengl/simplewaveformwidget.h" + +#include "waveform/renderers/qopengl/waveformrenderbackground.h" +#include "waveform/renderers/qopengl/waveformrenderbeat.h" +#include "waveform/renderers/qopengl/waveformrendererendoftrack.h" +#include "waveform/renderers/qopengl/waveformrendererpreroll.h" +#include "waveform/renderers/qopengl/waveformrenderersimple.h" +#include "waveform/renderers/qopengl/waveformrendermark.h" +#include "waveform/renderers/qopengl/waveformrendermarkrange.h" +#include "waveform/widgets/qopengl/moc_simplewaveformwidget.cpp" + +using namespace qopengl; + +SimpleWaveformWidget::SimpleWaveformWidget(const QString& group, QWidget* parent) + : WaveformWidget(group, parent) { + addRenderer<WaveformRenderBackground>(); + addRenderer<WaveformRendererEndOfTrack>(); + addRenderer<WaveformRendererPreroll>(); + addRenderer<WaveformRenderMarkRange>(); + addRenderer<WaveformRendererSimple>(); + addRenderer<WaveformRenderBeat>(); + addRenderer<WaveformRenderMark>(); + + m_initSuccess = init(); +} + +SimpleWaveformWidget::~SimpleWaveformWidget() { +} + +void SimpleWaveformWidget::castToQWidget() { + m_widget = this; +} + +void SimpleWaveformWidget::paintEvent(QPaintEvent* event) { + Q_UNUSED(event); +} diff --git a/src/waveform/widgets/qopengl/simplewaveformwidget.h b/src/waveform/widgets/qopengl/simplewaveformwidget.h new file mode 100644 index 0000000000..9dd3cf1ddb --- /dev/null +++ b/src/waveform/widgets/qopengl/simplewaveformwidget.h @@ -0,0 +1,46 @@ +#pragma once + +#include "util/class.h" +#include "waveform/widgets/qopengl/waveformwidget.h" + +class WaveformWidgetFactory; + +namespace qopengl { +class SimpleWaveformWidget; +} + +class qopengl::SimpleWaveformWidget final : public qopengl::WaveformWidget { + Q_OBJECT + public: + ~SimpleWaveformWidget() override; + + WaveformWidgetType::Type getType() const override { + return WaveformWidgetType::QOpenGLSimpleWaveform; + } + + static inline QString getWaveformWidgetName() { + return tr("Simple (QOpenGL)"); + } + static inline bool useOpenGl() { + return true; + } + static inline bool useOpenGles() { + return false; + } + static inline bool useOpenGLShaders() { + return true; + } + static inline bool developerOnly() { + return false; + } + + protected: + void castToQWidget() override; + void paintEvent(QPaintEvent* event) override; + + private: + SimpleWaveformWidget(const QString& group, QWidget* parent); + friend class ::WaveformWidgetFactory; + + DISALLOW_COPY_AND_ASSIGN(SimpleWaveformWidget); +}; diff --git a/src/waveform/widgets/waveformwidgettype.h b/src/waveform/widgets/waveformwidgettype.h index 956a20ab3d..16823ee88c 100644 --- a/src/waveform/widgets/waveformwidgettype.h +++ b/src/waveform/widgets/waveformwidgettype.h @@ -26,6 +26,7 @@ class WaveformWidgetType { QOpenGLRGBWaveform, // 17 RGB (QOpenGL) QOpenGLLRRGBWaveform, // 18 RGB (QOpenGL) QOpenGLFilteredWaveform, // 19 Filtered (QOpenGL) + QOpenGLSimpleWaveform, // 20 Simple (QOpenGL) #endif Count_WaveformwidgetType // Also used as invalid value }; |