Skip to content

Commit

Permalink
added simple qopengl waveform renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
m0dB committed Apr 15, 2023
1 parent 554c5cb commit 4906096
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 14 deletions.
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1159,13 +1159,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
Expand All @@ -1175,11 +1175,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
Expand Down
23 changes: 13 additions & 10 deletions src/waveform/renderers/qopengl/waveformrendererfiltered.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/waveform/renderers/qopengl/waveformrendererfiltered.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
5 changes: 4 additions & 1 deletion src/waveform/renderers/qopengl/waveformrendererlrrgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/waveform/renderers/qopengl/waveformrendererrgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
181 changes: 181 additions & 0 deletions src/waveform/renderers/qopengl/waveformrenderersimple.cpp
Original file line number Diff line number Diff line change
@@ -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();
}
28 changes: 28 additions & 0 deletions src/waveform/renderers/qopengl/waveformrenderersimple.h
Original file line number Diff line number Diff line change
@@ -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);
};
11 changes: 11 additions & 0 deletions src/waveform/waveformwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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)
Expand Down
36 changes: 36 additions & 0 deletions src/waveform/widgets/qopengl/simplewaveformwidget.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
Loading

0 comments on commit 4906096

Please sign in to comment.