Skip to content

Commit

Permalink
Add 3band glsl waveform renderer
Browse files Browse the repository at this point in the history
This waveform is a filterd, stacked 3 band rgb waveform.
Each band is rendered upon each other.
The filtered part of signal is drawn in a darker shade, in case
of gain, the waveform is extended.
The waveform is then mixed with a rgb mixed component color to smoothen
the look and allow easier detection of a hi-hat.
  • Loading branch information
poelzi committed Oct 5, 2020
1 parent ef58c52 commit 75281ea
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 28 deletions.
1 change: 1 addition & 0 deletions res/mixxx.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<file>shaders/filteredsignal.frag</file>
<file>shaders/passthrough.vert</file>
<file>shaders/rgbsignal.frag</file>
<file>shaders/3bandsignal.frag</file>
<file>skins/default.qss</file>
</qresource>
</RCC>
163 changes: 163 additions & 0 deletions res/shaders/3bandsignal.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#version 120

uniform vec2 framebufferSize;
uniform vec4 axesColor;
uniform vec4 lowColor;
uniform vec4 midColor;
uniform vec4 highColor;
uniform vec4 lowFilteredColor;
uniform vec4 midFilteredColor;
uniform vec4 highFilteredColor;

uniform int waveformLength;
uniform int textureSize;
uniform int textureStride;

uniform float allGain;
uniform float lowGain;
uniform float midGain;
uniform float highGain;
uniform float firstVisualIndex;
uniform float lastVisualIndex;

uniform sampler2D waveformDataTexture;

vec4 getWaveformData(float index) {
vec2 uv_data;
uv_data.y = floor(index / float(textureStride));
uv_data.x = floor(index - uv_data.y * float(textureStride));
// Divide again to convert to normalized UV coordinates.
return texture2D(waveformDataTexture, uv_data / float(textureStride));
}

bool nearBorder(float target, float test, float epsilon) {
float dist = target - test;
return (abs(dist) <= epsilon && dist > 0);
}

void main(void) {
vec2 uv = gl_TexCoord[0].st;
vec4 pixel = gl_FragCoord;

float new_currentIndex = floor(firstVisualIndex + uv.x *
(lastVisualIndex - firstVisualIndex)) * 2;

// Texture coordinates put (0,0) at the bottom left, so show the right
// channel if we are in the bottom half.
if (uv.y < 0.5) {
new_currentIndex += 1;
}

vec4 outputColor = vec4(0.0, 0.0, 0.0, 0.0);
bool showing = false;
bool showingUnscaled = false;
vec4 showingColor = vec4(0.0, 0.0, 0.0, 0.0);
vec4 mixColor = vec4(0.0, 0.0, 0.0, 0.0);
bool mixin = true;
float alpha = 0.75;

// We don't exit early if the waveform data is not valid because we may want
// to show other things (e.g. the axes lines) even when we are on a pixel
// that does not have valid waveform data.
if (new_currentIndex >= 0 && new_currentIndex <= waveformLength - 1) {
// Since the magnitude of the (low, mid, high) vector is used as the
// waveform height, re-scale the maximum height to 1.
const float scaleFactor = 1.0 / sqrt(3.0);

vec4 new_currentDataUnscaled = getWaveformData(new_currentIndex) * allGain;
new_currentDataUnscaled.x *= scaleFactor;
new_currentDataUnscaled.y *= scaleFactor;
new_currentDataUnscaled.z *= scaleFactor;

vec4 new_currentData = new_currentDataUnscaled;
new_currentData.x *= lowGain;
new_currentData.y *= midGain;
new_currentData.z *= highGain;

vec4 new_currentDataTop = vec4(0.0, 0.0, 0.0, 0.0);
new_currentDataTop.x = max(new_currentData.x, new_currentDataUnscaled.x);
new_currentDataTop.y = max(new_currentData.y, new_currentDataUnscaled.y);
new_currentDataTop.z = max(new_currentData.z, new_currentDataUnscaled.z);



//(vrince) debug see pre-computed signal
//gl_FragColor = new_currentData;
//return;

// Represents the [-1, 1] distance of this pixel. Subtracting this from
// the signal data in new_currentData, we can tell if a signal band should
// show in this pixel if the component is > 0.
float ourDistance = abs((uv.y - 0.5) * 2.0);

float signalDistance = sqrt(new_currentData.x * new_currentData.x +
new_currentData.y * new_currentData.y +
new_currentData.z * new_currentData.z) *
scaleFactor;

bool drawBorder = false;
showing = true;
if (drawBorder && nearBorder(new_currentDataUnscaled.x, ourDistance, 0.04)) {
showingColor = lowColor;
mixin = false;
alpha = 0.90;
} else if (ourDistance <= new_currentData.x) {
showingColor = lowColor;
} else if (ourDistance <= new_currentDataUnscaled.x) {
showingColor = lowFilteredColor;
alpha = 0.6;
} else if (drawBorder && nearBorder(new_currentDataUnscaled.x + new_currentDataUnscaled.y, ourDistance, 0.04)) {
showingColor = midColor;
mixin = false;
alpha = 0.90;
} else if (ourDistance <= new_currentDataTop.x + new_currentData.y) {
showingColor = midColor;
} else if (ourDistance <= new_currentDataTop.x + new_currentDataTop.y) {
showingColor = midFilteredColor;
alpha = 0.6;
} else if (drawBorder && nearBorder(new_currentDataTop.x + new_currentDataTop.y + new_currentDataUnscaled.z, ourDistance, 0.04)) {
showingColor = highColor;
mixin = false;
alpha = 0.90;
} else if (ourDistance <= new_currentDataTop.x + new_currentDataTop.y + new_currentData.z) {
showingColor = highColor;
} else if (ourDistance <= new_currentDataTop.x + new_currentDataTop.y + new_currentDataUnscaled.z) {
showingColor = highFilteredColor;
alpha = 0.6;
} else {
showing = false;
}

// Linearly combine the low, mid, and high colors according to the low,
// mid, and high components of the unfiltered signal.
mixColor = lowColor * new_currentDataUnscaled.x +
midColor * new_currentDataUnscaled.y +
highColor * new_currentDataUnscaled.z;

// Re-scale the color by the maximum component.
float showingMax = max(mixColor.x, max(mixColor.y, mixColor.z));
mixColor = mixColor / showingMax;
mixColor.w = 1.0;
}
// Draw the axes color as the lowest item on the screen.
// TODO(owilliams): The "4" in this line makes sure the axis gets
// rendered even when the waveform is fairly short. Really this
// value should be based on the size of the widget.
if (abs(framebufferSize.y / 2 - pixel.y) <= 4) {
outputColor.xyz = mix(outputColor.xyz, axesColor.xyz, axesColor.w);
outputColor.w = 1.0;
}

if (showing) {
outputColor.xyz = mix(outputColor.xyz, showingColor.xyz, alpha);
// we mix in the sum color to smoothen the look and give it the
// general color tone.
if (mixin == true) {
outputColor.xyz = mix(outputColor.xyz, mixColor.xyz, 0.53f);
}
outputColor.w = 1.0;
}

gl_FragColor = outputColor;

}
46 changes: 29 additions & 17 deletions src/waveform/renderers/glslwaveformrenderersignal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
#include "waveform/waveformwidgetfactory.h"

GLSLWaveformRendererSignal::GLSLWaveformRendererSignal(WaveformWidgetRenderer* waveformWidgetRenderer,
bool rgbShader)
ColorType colorType,
const char *fragShader)
: WaveformRendererSignalBase(waveformWidgetRenderer),
m_unitQuadListId(-1),
m_textureId(0),
m_textureRenderedWaveformCompletion(0),
m_bDumpPng(false),
m_shadersValid(false),
m_rgbShader(rgbShader) {
m_colorType(colorType),
m_pFragShader(fragShader) {
initializeOpenGLFunctions();
}

Expand Down Expand Up @@ -51,9 +53,8 @@ bool GLSLWaveformRendererSignal::loadShaders() {
<< m_frameShaderProgram->log();
return false;
}
QString fragmentShader = m_rgbShader ?
":/shaders/rgbsignal.frag" :
":/shaders/filteredsignal.frag";
QString fragmentShader = QString(m_pFragShader);

if (!m_frameShaderProgram->addShaderFromSourceFile(
QGLShader::Fragment, fragmentShader)) {
qDebug() << "GLWaveformRendererSignalShader::loadShaders - "
Expand Down Expand Up @@ -331,18 +332,29 @@ void GLSLWaveformRendererSignal::draw(QPainter* painter, QPaintEvent* /*event*/)
m_frameShaderProgram->setUniformValue("axesColor", QVector4D(m_axesColor_r, m_axesColor_g,
m_axesColor_b, m_axesColor_a));

QVector4D lowColor = m_rgbShader ?
QVector4D(m_rgbLowColor_r, m_rgbLowColor_g, m_rgbLowColor_b, 1.0) :
QVector4D(m_lowColor_r, m_lowColor_g, m_lowColor_b, 1.0);
QVector4D midColor = m_rgbShader ?
QVector4D(m_rgbMidColor_r, m_rgbMidColor_g, m_rgbMidColor_b, 1.0) :
QVector4D(m_midColor_r, m_midColor_g, m_midColor_b, 1.0);
QVector4D highColor = m_rgbShader ?
QVector4D(m_rgbHighColor_r, m_rgbHighColor_g, m_rgbHighColor_b, 1.0) :
QVector4D(m_highColor_r, m_highColor_g, m_highColor_b, 1.0);
m_frameShaderProgram->setUniformValue("lowColor", lowColor);
m_frameShaderProgram->setUniformValue("midColor", midColor);
m_frameShaderProgram->setUniformValue("highColor", highColor);
if (m_colorType == ColorType::RGBFiltered) {
QVector4D lowFilteredColor = QVector4D(m_rgbLowFilteredColor_r, m_rgbLowFilteredColor_g, m_rgbLowFilteredColor_b, 1.0);
QVector4D midFilteredColor = QVector4D(m_rgbMidFilteredColor_r, m_rgbMidFilteredColor_g, m_rgbMidFilteredColor_b, 1.0);
QVector4D highFilteredColor = QVector4D(m_rgbHighFilteredColor_r, m_rgbHighFilteredColor_g, m_rgbHighFilteredColor_b, 1.0);
m_frameShaderProgram->setUniformValue("lowFilteredColor", lowFilteredColor);
m_frameShaderProgram->setUniformValue("midFilteredColor", midFilteredColor);
m_frameShaderProgram->setUniformValue("highFilteredColor", highFilteredColor);
}
if (m_colorType == ColorType::RGB || m_colorType == ColorType::RGBFiltered) {
QVector4D lowColor = QVector4D(m_rgbLowColor_r, m_rgbLowColor_g, m_rgbLowColor_b, 1.0);
QVector4D midColor = QVector4D(m_rgbMidColor_r, m_rgbMidColor_g, m_rgbMidColor_b, 1.0);
QVector4D highColor = QVector4D(m_rgbHighColor_r, m_rgbHighColor_g, m_rgbHighColor_b, 1.0);
m_frameShaderProgram->setUniformValue("lowColor", lowColor);
m_frameShaderProgram->setUniformValue("midColor", midColor);
m_frameShaderProgram->setUniformValue("highColor", highColor);
} else {
QVector4D lowColor = QVector4D(m_lowColor_r, m_lowColor_g, m_lowColor_b, 1.0);
QVector4D midColor = QVector4D(m_midColor_r, m_midColor_g, m_midColor_b, 1.0);
QVector4D highColor = QVector4D(m_highColor_r, m_highColor_g, m_highColor_b, 1.0);
m_frameShaderProgram->setUniformValue("lowColor", lowColor);
m_frameShaderProgram->setUniformValue("midColor", midColor);
m_frameShaderProgram->setUniformValue("highColor", highColor);
}

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, m_textureId);
Expand Down
25 changes: 21 additions & 4 deletions src/waveform/renderers/glslwaveformrenderersignal.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ class GLSLWaveformRendererSignal: public QObject,
protected QOpenGLFunctions_2_1 {
Q_OBJECT
public:
enum class ColorType {
Filtered,
RGB,
RGBFiltered,
};

GLSLWaveformRendererSignal(WaveformWidgetRenderer* waveformWidgetRenderer,
bool rgbShader);
ColorType colorType,
const char *fragShader);
~GLSLWaveformRendererSignal() override;

bool onInit() override;
Expand Down Expand Up @@ -51,15 +58,16 @@ class GLSLWaveformRendererSignal: public QObject,

// shaders
bool m_shadersValid;
bool m_rgbShader;
ColorType m_colorType;
const char *m_pFragShader;
std::unique_ptr<QGLShaderProgram> m_frameShaderProgram;
};

class GLSLWaveformRendererFilteredSignal: public GLSLWaveformRendererSignal {
public:
GLSLWaveformRendererFilteredSignal(
WaveformWidgetRenderer* waveformWidgetRenderer) :
GLSLWaveformRendererSignal(waveformWidgetRenderer, false) {
GLSLWaveformRendererSignal(waveformWidgetRenderer, ColorType::Filtered, ":/shaders/filteredsignal.frag") {
}
~GLSLWaveformRendererFilteredSignal() override {
}
Expand All @@ -69,8 +77,17 @@ class GLSLWaveformRendererRGBSignal : public GLSLWaveformRendererSignal {
public:
GLSLWaveformRendererRGBSignal(
WaveformWidgetRenderer* waveformWidgetRenderer)
: GLSLWaveformRendererSignal(waveformWidgetRenderer, true) {}
: GLSLWaveformRendererSignal(waveformWidgetRenderer, ColorType::RGB, ":/shaders/rgbsignal.frag") {}
~GLSLWaveformRendererRGBSignal() override {}
};

class GLSLWaveformRenderer3BandSignal : public GLSLWaveformRendererSignal {
public:
GLSLWaveformRenderer3BandSignal(
WaveformWidgetRenderer* waveformWidgetRenderer)
: GLSLWaveformRendererSignal(waveformWidgetRenderer, ColorType::RGBFiltered, ":/shaders/3bandsignal.frag") {}
~GLSLWaveformRenderer3BandSignal() override {}
};


#endif // QT_NO_OPENGL && !QT_OPENGL_ES_2
9 changes: 9 additions & 0 deletions src/waveform/renderers/waveformrenderersignalbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ void WaveformRendererSignalBase::setup(const QDomNode& node,
const QColor& rgbHigh = m_pColors->getRgbHighColor();
rgbHigh.getRgbF(&m_rgbHighColor_r, &m_rgbHighColor_g, &m_rgbHighColor_b);

const QColor& rgbFilteredLow = m_pColors->getRgbLowFilteredColor();
rgbFilteredLow.getRgbF(&m_rgbLowFilteredColor_r, &m_rgbLowFilteredColor_g, &m_rgbLowFilteredColor_b);

const QColor& rgbFilteredMid = m_pColors->getRgbMidFilteredColor();
rgbFilteredMid.getRgbF(&m_rgbMidFilteredColor_r, &m_rgbMidFilteredColor_g, &m_rgbMidFilteredColor_b);

const QColor& rgbFilteredHigh = m_pColors->getRgbHighFilteredColor();
rgbFilteredHigh.getRgbF(&m_rgbHighFilteredColor_r, &m_rgbHighFilteredColor_g, &m_rgbHighFilteredColor_b);

const QColor& axes = m_pColors->getAxesColor();
axes.getRgbF(&m_axesColor_r, &m_axesColor_g, &m_axesColor_b,
&m_axesColor_a);
Expand Down
3 changes: 3 additions & 0 deletions src/waveform/renderers/waveformrenderersignalbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class WaveformRendererSignalBase : public WaveformRendererAbstract {
qreal m_rgbLowColor_r, m_rgbLowColor_g, m_rgbLowColor_b;
qreal m_rgbMidColor_r, m_rgbMidColor_g, m_rgbMidColor_b;
qreal m_rgbHighColor_r, m_rgbHighColor_g, m_rgbHighColor_b;
qreal m_rgbLowFilteredColor_r, m_rgbLowFilteredColor_g, m_rgbLowFilteredColor_b;
qreal m_rgbMidFilteredColor_r, m_rgbMidFilteredColor_g, m_rgbMidFilteredColor_b;
qreal m_rgbHighFilteredColor_r, m_rgbHighFilteredColor_g, m_rgbHighFilteredColor_b;
};

#endif // WAVEFORMRENDERERSIGNALBASE_H
19 changes: 19 additions & 0 deletions src/waveform/renderers/waveformsignalcolors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ bool WaveformSignalColors::setup(const QDomNode &node, const SkinContext& contex
}
m_rgbHighColor = WSkinColor::getCorrectColor(m_rgbHighColor).toRgb();

// filtered colors
m_rgbLowFilteredColor.setNamedColor(context.selectString(node, "SignalRGBLowFilteredColor"));
if (!m_rgbLowFilteredColor.isValid()) {
m_rgbLowFilteredColor = m_rgbLowColor.darker(300);
}
m_rgbLowFilteredColor = WSkinColor::getCorrectColor(m_rgbLowFilteredColor).toRgb();

m_rgbMidFilteredColor.setNamedColor(context.selectString(node, "SignalRGBMidFilteredColor"));
if (!m_rgbMidFilteredColor.isValid()) {
m_rgbMidFilteredColor = m_rgbMidColor.darker(300);;
}
m_rgbMidFilteredColor = WSkinColor::getCorrectColor(m_rgbMidFilteredColor).toRgb();

m_rgbHighFilteredColor.setNamedColor(context.selectString(node, "SignalRGBHighFilteredColor"));
if (!m_rgbHighFilteredColor.isValid()) {
m_rgbHighFilteredColor = m_rgbHighColor.darker(300);;
}
m_rgbHighFilteredColor = WSkinColor::getCorrectColor(m_rgbHighFilteredColor).toRgb();

m_axesColor = context.selectColor(node, "AxesColor");
if (!m_axesColor.isValid()) {
m_axesColor = QColor(245,245,245);
Expand Down
6 changes: 6 additions & 0 deletions src/waveform/renderers/waveformsignalcolors.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class WaveformSignalColors {
inline const QColor& getRgbLowColor() const { return m_rgbLowColor; }
inline const QColor& getRgbMidColor() const { return m_rgbMidColor; }
inline const QColor& getRgbHighColor() const { return m_rgbHighColor; }
inline const QColor& getRgbLowFilteredColor() const { return m_rgbLowFilteredColor; }
inline const QColor& getRgbMidFilteredColor() const { return m_rgbMidFilteredColor; }
inline const QColor& getRgbHighFilteredColor() const { return m_rgbHighFilteredColor; }
inline const QColor& getAxesColor() const { return m_axesColor; }
inline const QColor& getPlayPosColor() const { return m_playPosColor; }
inline const QColor& getPlayedOverlayColor() const { return m_playedOverlayColor; }
Expand All @@ -40,6 +43,9 @@ class WaveformSignalColors {
QColor m_rgbLowColor;
QColor m_rgbMidColor;
QColor m_rgbHighColor;
QColor m_rgbLowFilteredColor;
QColor m_rgbMidFilteredColor;
QColor m_rgbHighFilteredColor;
QColor m_axesColor;
QColor m_playPosColor;
QColor m_playedOverlayColor;
Expand Down
10 changes: 10 additions & 0 deletions src/waveform/waveformwidgetfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,13 @@ void WaveformWidgetFactory::evaluateWidgets() {
useOpenGLShaders = GLSLRGBWaveformWidget::useOpenGLShaders();
developerOnly = GLSLRGBWaveformWidget::developerOnly();
break;
case WaveformWidgetType::GLSLRGB3BandWaveform:
widgetName = GLSLRGB3BandWaveformWidget::getWaveformWidgetName();
useOpenGl = GLSLRGB3BandWaveformWidget::useOpenGl();
useOpenGles = GLSLRGB3BandWaveformWidget::useOpenGles();
useOpenGLShaders = GLSLRGB3BandWaveformWidget::useOpenGLShaders();
developerOnly = GLSLRGB3BandWaveformWidget::developerOnly();
break;
case WaveformWidgetType::GLVSyncTest:
widgetName = GLVSyncTestWidget::getWaveformWidgetName();
useOpenGl = GLVSyncTestWidget::useOpenGl();
Expand Down Expand Up @@ -944,6 +951,9 @@ WaveformWidgetAbstract* WaveformWidgetFactory::createWaveformWidget(
case WaveformWidgetType::GLSLRGBWaveform:
widget = new GLSLRGBWaveformWidget(viewer->getGroup(), viewer);
break;
case WaveformWidgetType::GLSLRGB3BandWaveform:
widget = new GLSLRGB3BandWaveformWidget(viewer->getGroup(), viewer);
break;
case WaveformWidgetType::GLVSyncTest:
widget = new GLVSyncTestWidget(viewer->getGroup(), viewer);
break;
Expand Down
Loading

0 comments on commit 75281ea

Please sign in to comment.