Skip to content

Commit

Permalink
Fix shifting of sample waveform during zoom (#7222)
Browse files Browse the repository at this point in the history
The original code was doing division in `int`, which causes lost of accuracy and results in the waveform randomly shifting when zooming in and out.

This should fix it by casting variables to `float` before dividing, as well as keeping the values in `float` type.

The pull request also changes some type declaration to explicit to increase readability
  • Loading branch information
khoidauminh authored May 2, 2024
1 parent 9ca9143 commit d5f5d00
Showing 1 changed file with 34 additions and 23 deletions.
57 changes: 34 additions & 23 deletions src/gui/SampleWaveform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,55 +28,66 @@ namespace lmms::gui {

void SampleWaveform::visualize(Parameters parameters, QPainter& painter, const QRect& rect)
{
const auto x = rect.x();
const auto height = rect.height();
const auto width = rect.width();
const auto centerY = rect.center().y();
const int x = rect.x();
const int height = rect.height();
const int width = rect.width();
const int centerY = rect.center().y();

const auto halfHeight = height / 2;
const int halfHeight = height / 2;

const auto color = painter.pen().color();
const auto rmsColor = color.lighter(123);

const auto framesPerPixel = std::max<size_t>(1, parameters.size / width);
const float framesPerPixel = std::max(1.0f, static_cast<float>(parameters.size) / width);

constexpr auto maxFramesPerPixel = 512;
const auto resolution = std::max<size_t>(1, framesPerPixel / maxFramesPerPixel);
const auto framesPerResolution = framesPerPixel / resolution;
constexpr float maxFramesPerPixel = 512.0f;
const float resolution = std::max(1.0f, framesPerPixel / maxFramesPerPixel);
const float framesPerResolution = framesPerPixel / resolution;

const auto numPixels = std::min<size_t>(parameters.size, width);
const size_t numPixels = std::min<size_t>(parameters.size, width);
auto min = std::vector<float>(numPixels, 1);
auto max = std::vector<float>(numPixels, -1);
auto squared = std::vector<float>(numPixels);
auto squared = std::vector<float>(numPixels, 0);

const size_t maxFrames = numPixels * static_cast<size_t>(framesPerPixel);

int pixelIndex = 0;

const auto maxFrames = numPixels * framesPerPixel;
for (int i = 0; i < maxFrames; i += resolution)
{
const auto pixelIndex = i / framesPerPixel;
const auto frameIndex = !parameters.reversed ? i : maxFrames - i;
pixelIndex = i / framesPerPixel;
const int frameIndex = !parameters.reversed ? i : maxFrames - i;

const auto& frame = parameters.buffer[frameIndex];
const auto value = std::accumulate(frame.begin(), frame.end(), 0.0f) / frame.size();
const float value = std::accumulate(frame.begin(), frame.end(), 0.0f) / frame.size();

if (value > max[pixelIndex]) { max[pixelIndex] = value; }
if (value < min[pixelIndex]) { min[pixelIndex] = value; }

squared[pixelIndex] += value * value;
}

while (pixelIndex < numPixels)
{
max[pixelIndex] = 0.0;
min[pixelIndex] = 0.0;

pixelIndex++;
}

for (int i = 0; i < numPixels; i++)
{
const auto lineY1 = centerY - max[i] * halfHeight * parameters.amplification;
const auto lineY2 = centerY - min[i] * halfHeight * parameters.amplification;
const auto lineX = i + x;
const int lineY1 = centerY - max[i] * halfHeight * parameters.amplification;
const int lineY2 = centerY - min[i] * halfHeight * parameters.amplification;
const int lineX = i + x;
painter.drawLine(lineX, lineY1, lineX, lineY2);

const auto rms = std::sqrt(squared[i] / framesPerResolution);
const auto maxRMS = std::clamp(rms, min[i], max[i]);
const auto minRMS = std::clamp(-rms, min[i], max[i]);
const float rms = std::sqrt(squared[i] / framesPerResolution);
const float maxRMS = std::clamp(rms, min[i], max[i]);
const float minRMS = std::clamp(-rms, min[i], max[i]);

const auto rmsLineY1 = centerY - maxRMS * halfHeight * parameters.amplification;
const auto rmsLineY2 = centerY - minRMS * halfHeight * parameters.amplification;
const int rmsLineY1 = centerY - maxRMS * halfHeight * parameters.amplification;
const int rmsLineY2 = centerY - minRMS * halfHeight * parameters.amplification;

painter.setPen(rmsColor);
painter.drawLine(lineX, rmsLineY1, lineX, rmsLineY2);
Expand Down

0 comments on commit d5f5d00

Please sign in to comment.