Skip to content

Commit

Permalink
WIP - SlipMode waveform visual
Browse files Browse the repository at this point in the history
  • Loading branch information
acolombier committed Mar 25, 2024
1 parent 3aa73fd commit 8a58627
Show file tree
Hide file tree
Showing 12 changed files with 394 additions and 57 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,7 @@ endif()
if(QOPENGL)
target_sources(mixxx-lib PRIVATE
src/shaders/endoftrackshader.cpp
src/shaders/slipmodeshader.cpp
src/shaders/patternshader.cpp
src/shaders/rgbashader.cpp
src/shaders/rgbshader.cpp
Expand All @@ -1510,6 +1511,7 @@ if(QOPENGL)
src/waveform/renderers/allshader/waveformrenderbeat.cpp
src/waveform/renderers/allshader/waveformrenderer.cpp
src/waveform/renderers/allshader/waveformrendererendoftrack.cpp
src/waveform/renderers/allshader/waveformrendererslipmode.cpp
src/waveform/renderers/allshader/waveformrendererfiltered.cpp
src/waveform/renderers/allshader/waveformrendererhsv.cpp
src/waveform/renderers/allshader/waveformrendererlrrgb.cpp
Expand Down
49 changes: 49 additions & 0 deletions src/shaders/slipmodeshader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include "shaders/slipmodeshader.h"

using namespace mixxx;

void SlipModeShader::init() {
QString vertexShaderCode = QStringLiteral(R"--(
#version 150
attribute highp vec4 position; // use vec4 here (will be padded) to assign directly to gl_Position
out highp vec4 vposition;
void main()
{
vposition = position;
gl_Position = position;
}
)--");

QString fragmentShaderCode = QStringLiteral(R"--(
#version 120
uniform highp vec4 color;
uniform highp vec2 dimension;
in highp vec4 vposition;
void main()
{
float xBorder = abs(dimension.x*vposition.x);
float yBorder = dimension.y*vposition.y;
gl_FragColor = vec4(0, 0, 0, 0);
if( (xBorder > dimension.x - 10 && yBorder >= 0) || yBorder < 0 || yBorder > dimension.y - 10)
{
float borderAlpha = max(
yBorder < 0 ? 0 : max(0, xBorder - dimension.x + 10),
yBorder < 0 ? max(0, 10 + yBorder) : max(0, yBorder - dimension.y + 10)
) / 10;
gl_FragColor = vec4(color.xyz, min(color.w, mix(0, 1, borderAlpha)));
}
}
)--");

load(vertexShaderCode, fragmentShaderCode);

m_positionLocation = attributeLocation("position");
m_dimensionLocation = uniformLocation("dimension");
m_colorLocation = uniformLocation("color");
}
31 changes: 31 additions & 0 deletions src/shaders/slipmodeshader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "shaders/shader.h"

namespace mixxx {
class SlipModeShader;
}

class mixxx::SlipModeShader : public mixxx::Shader {
public:
SlipModeShader() = default;
~SlipModeShader() = default;
void init();

int positionLocation() const {
return m_positionLocation;
}
int dimensionLocation() const {
return m_dimensionLocation;
}
int colorLocation() const {
return m_colorLocation;
}

private:
int m_positionLocation;
int m_dimensionLocation;
int m_colorLocation;

DISALLOW_COPY_AND_ASSIGN(SlipModeShader)
};
39 changes: 38 additions & 1 deletion src/waveform/renderers/allshader/waveformrenderbeat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,19 @@ void WaveformRenderBeat::paintGL() {
m_waveformRenderer->getFirstDisplayedPosition();
const double lastDisplayedPosition =
m_waveformRenderer->getLastDisplayedPosition();
const double firstSlipDisplayedPosition =
m_waveformRenderer->getDisplayedSlipPosition();
const double lastSlipDisplayedPosition =
firstSlipDisplayedPosition + (lastDisplayedPosition - firstDisplayedPosition);

const auto startPosition = mixxx::audio::FramePos::fromEngineSamplePos(
firstDisplayedPosition * trackSamples);
const auto endPosition = mixxx::audio::FramePos::fromEngineSamplePos(
lastDisplayedPosition * trackSamples);
const auto startSlipPosition = mixxx::audio::FramePos::fromEngineSamplePos(
firstSlipDisplayedPosition * trackSamples);
const auto endSlipPosition = mixxx::audio::FramePos::fromEngineSamplePos(
lastSlipDisplayedPosition * trackSamples);

if (!startPosition.isValid() || !endPosition.isValid()) {
return;
Expand All @@ -81,11 +89,40 @@ void WaveformRenderBeat::paintGL() {
++it) {
numBeatsInRange++;
}
if (firstSlipDisplayedPosition != firstDisplayedPosition) {
for (auto it = trackBeats->iteratorFrom(startSlipPosition);
it != trackBeats->cend() && *it <= endSlipPosition;
++it) {
numBeatsInRange++;
}
}

const int reserved = numBeatsInRange * numVerticesPerLine;
m_vertices.clear();
m_vertices.reserve(reserved);

if (firstSlipDisplayedPosition != firstDisplayedPosition) {
for (auto it = trackBeats->iteratorFrom(startSlipPosition);
it != trackBeats->cend() && *it <= endSlipPosition;
++it) {
double beatPosition = it->toEngineSamplePos();
double xBeatPoint =
m_waveformRenderer->transformSampleSlipPositionInRendererWorld(beatPosition);

xBeatPoint = qRound(xBeatPoint * devicePixelRatio) / devicePixelRatio;

const float x1 = static_cast<float>(xBeatPoint);
const float x2 = x1 + 1.f;

m_vertices.addRectangle(x1, 0.f, x2, rendererBreadth / 2.f);
}
}

const float originRendererBreadth =
firstSlipDisplayedPosition != firstDisplayedPosition
? rendererBreadth / 2.f
: 0.f;

for (auto it = trackBeats->iteratorFrom(startPosition);
it != trackBeats->cend() && *it <= endPosition;
++it) {
Expand All @@ -98,7 +135,7 @@ void WaveformRenderBeat::paintGL() {
const float x1 = static_cast<float>(xBeatPoint);
const float x2 = x1 + 1.f;

m_vertices.addRectangle(x1, 0.f, x2, rendererBreadth);
m_vertices.addRectangle(x1, originRendererBreadth, x2, rendererBreadth);
}

DEBUG_ASSERT(reserved == m_vertices.size());
Expand Down
119 changes: 72 additions & 47 deletions src/waveform/renderers/allshader/waveformrendererrgb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ void WaveformRendererRGB::paintGL() {
m_waveformRenderer->getFirstDisplayedPosition() * visualFramesSize;
const double lastVisualFrame =
m_waveformRenderer->getLastDisplayedPosition() * visualFramesSize;
const double slipFirstVisualIndex =
m_waveformRenderer->getDisplayedSlipPosition() * visualFramesSize;

// Represents the # of visual frames per horizontal pixel.
const double visualIncrementPerPixel =
Expand Down Expand Up @@ -86,10 +88,14 @@ void WaveformRendererRGB::paintGL() {
// Effective visual frame for x
double xVisualFrame = qRound(firstVisualFrame / visualIncrementPerPixel) *
visualIncrementPerPixel;
double xSlipVisualOffset =
qRound((slipFirstVisualIndex - firstVisualFrame) /
visualIncrementPerPixel) *
visualIncrementPerPixel;

const int numVerticesPerLine = 6; // 2 triangles

const int reserved = numVerticesPerLine * (length + 1);
const int reserved = numVerticesPerLine * (2 * length + 1);

m_vertices.clear();
m_vertices.reserve(reserved);
Expand All @@ -107,6 +113,8 @@ void WaveformRendererRGB::paintGL() {

const double maxSamplingRange = visualIncrementPerPixel / 2.0;

const int slipOffset = std::lround(xSlipVisualOffset) * 2;

for (int pos = 0; pos < length; ++pos) {
const int visualFrameStart = std::lround(xVisualFrame - maxSamplingRange);
const int visualFrameStop = std::lround(xVisualFrame + maxSamplingRange);
Expand All @@ -119,79 +127,96 @@ void WaveformRendererRGB::paintGL() {

// Find the max values for low, mid, high and all in the waveform data.
// - Max of left and right
uchar u8maxLow{};
uchar u8maxMid{};
uchar u8maxHigh{};
uchar u8maxLow[2]{};
uchar u8maxMid[2]{};
uchar u8maxHigh[2]{};
// - Per channel
uchar u8maxAllChn[2]{};
for (int chn = 0; chn < 2; chn++) {
// data is interleaved left / right
for (int i = visualIndexStart + chn; i < visualIndexStop + chn; i += 2) {
const WaveformData& waveformData = data[i];

u8maxLow = math_max(u8maxLow, waveformData.filtered.low);
u8maxMid = math_max(u8maxMid, waveformData.filtered.mid);
u8maxHigh = math_max(u8maxHigh, waveformData.filtered.high);
u8maxAllChn[chn] = math_max(u8maxAllChn[chn], waveformData.filtered.all);
}
// for (int chn = 0; chn < 2; chn++) {
// data is interleaved left / right
for (int i = visualIndexStart; i < visualIndexStop; i += 2) {
const WaveformData& waveformData = data[i + slipOffset];

u8maxLow[0] = math_max(u8maxLow[0], waveformData.filtered.low);
u8maxMid[0] = math_max(u8maxMid[0], waveformData.filtered.mid);
u8maxHigh[0] = math_max(u8maxHigh[0], waveformData.filtered.high);
u8maxAllChn[0] = math_max(u8maxAllChn[0], waveformData.filtered.all);

u8maxLow[1] = math_max(u8maxLow[1], data[i + 1].filtered.low);
u8maxMid[1] = math_max(u8maxMid[1], data[i + 1].filtered.mid);
u8maxHigh[1] = math_max(u8maxHigh[1], data[i + 1].filtered.high);
u8maxAllChn[1] = math_max(u8maxAllChn[1], data[i + 1].filtered.all);
}
// }

// Cast to float
float maxLow = static_cast<float>(u8maxLow);
float maxMid = static_cast<float>(u8maxMid);
float maxHigh = static_cast<float>(u8maxHigh);
float maxLow[2]{static_cast<float>(u8maxLow[0]), static_cast<float>(u8maxLow[1])};
float maxMid[2]{static_cast<float>(u8maxMid[0]), static_cast<float>(u8maxMid[1])};
float maxHigh[2]{static_cast<float>(u8maxHigh[0]), static_cast<float>(u8maxHigh[1])};
float maxAllChn[2]{static_cast<float>(u8maxAllChn[0]), static_cast<float>(u8maxAllChn[1])};
// Uncomment to undo scaling with pow(value, 2.0f * 0.316f) done in analyzerwaveform.h
// float maxAllChn[2]{unscale(u8maxAllChn[0]), unscale(u8maxAllChn[1])};

// Calculate the squared magnitude of the maxLow, maxMid and maxHigh values.
// We take the square root to get the magnitude below.
const float sum = math_pow2(maxLow) + math_pow2(maxMid) + math_pow2(maxHigh);
const float sum[2]{
math_pow2(maxLow[0]) + math_pow2(maxMid[0]) + math_pow2(maxHigh[0]),
math_pow2(maxLow[1]) + math_pow2(maxMid[1]) + math_pow2(maxHigh[1])};

// Apply the gains
maxLow *= lowGain;
maxMid *= midGain;
maxHigh *= highGain;
for (int chn = 0; chn < 2; chn++) {
maxLow[chn] *= lowGain;
maxMid[chn] *= midGain;
maxHigh[chn] *= highGain;
}

// Calculate the squared magnitude of the gained maxLow, maxMid and maxHigh values
// We take the square root to get the magnitude below.
const float sumGained = math_pow2(maxLow) + math_pow2(maxMid) + math_pow2(maxHigh);
const float sumGained[2]{
math_pow2(maxLow[0]) + math_pow2(maxMid[0]) + math_pow2(maxHigh[0]),
math_pow2(maxLow[1]) + math_pow2(maxMid[1]) + math_pow2(maxHigh[1])};

// The maxAll values will be used to draw the amplitude. We scale them according to
// magnitude of the gained maxLow, maxMid and maxHigh values
if (sum != 0.f) {
// magnitude = sqrt(sum) and magnitudeGained = sqrt(sumGained), and
// factor = magnitudeGained / magnitude, but we can do with a single sqrt:
const float factor = std::sqrt(sumGained / sum);
maxAllChn[0] *= factor;
maxAllChn[1] *= factor;
}
for (int chn = 0; chn < 2; chn++) {
if (sum[chn] != 0.f) {
// magnitude = sqrt(sum) and magnitudeGained = sqrt(sumGained), and
// factor = magnitudeGained / magnitude, but we can do with a single sqrt:
const float factor = std::sqrt(sumGained[chn] / sum[chn]);
maxAllChn[0] *= factor;
maxAllChn[1] *= factor;
}

// Use the gained maxLow, maxMid and maxHigh values to calculate the color components
float red = maxLow * low_r + maxMid * mid_r + maxHigh * high_r;
float green = maxLow * low_g + maxMid * mid_g + maxHigh * high_g;
float blue = maxLow * low_b + maxMid * mid_b + maxHigh * high_b;

// Normalize the color components using the maximum of the three
const float maxComponent = math_max3(red, green, blue);
if (maxComponent == 0.f) {
// Avoid division by 0
red = 0.f;
green = 0.f;
blue = 0.f;
} else {
const float normFactor = 1.f / maxComponent;
red *= normFactor;
green *= normFactor;
blue *= normFactor;
// Use the gained maxLow, maxMid and maxHigh values to calculate the color components
float red = maxLow[chn] * low_r + maxMid[chn] * mid_r + maxHigh[chn] * high_r;
float green = maxLow[chn] * low_g + maxMid[chn] * mid_g + maxHigh[chn] * high_g;
float blue = maxLow[chn] * low_b + maxMid[chn] * mid_b + maxHigh[chn] * high_b;

// Normalize the color components using the maximum of the three
const float maxComponent = math_max3(red, green, blue);
if (maxComponent == 0.f) {
// Avoid division by 0
red = 0.f;
green = 0.f;
blue = 0.f;
} else {
const float normFactor = 1.f / maxComponent;
red *= normFactor;
green *= normFactor;
blue *= normFactor;
}
m_colors.addForRectangle(red, green, blue);
}

// Lines are thin rectangles
m_vertices.addRectangle(fpos - 0.5f,
halfBreadth - heightFactor * maxAllChn[0],
fpos + 0.5f,
halfBreadth);
m_vertices.addRectangle(fpos - 0.5f,
halfBreadth,
fpos + 0.5f,
halfBreadth + heightFactor * maxAllChn[1]);
m_colors.addForRectangle(red, green, blue);

xVisualFrame += visualIncrementPerPixel;
}
Expand Down
Loading

0 comments on commit 8a58627

Please sign in to comment.