diff --git a/src/gui/SampleWaveform.cpp b/src/gui/SampleWaveform.cpp index 165ede4ee89..7849ae8259f 100644 --- a/src/gui/SampleWaveform.cpp +++ b/src/gui/SampleWaveform.cpp @@ -28,64 +28,39 @@ namespace lmms::gui { void SampleWaveform::visualize(Parameters parameters, QPainter& painter, const QRect& rect) { - const int x = rect.x(); - const int height = rect.height(); - const int width = rect.width(); - const int centerY = rect.center().y(); + if (!parameters.buffer || parameters.size == 0) { return; } - const int halfHeight = height / 2; + const auto centerY = rect.center().y(); + const auto halfHeight = rect.height() / 2; const auto color = painter.pen().color(); const auto rmsColor = color.lighter(123); - const float framesPerPixel = std::max(1.0f, static_cast(parameters.size) / width); + const auto samplesPerPixel = std::max(1.0f, static_cast(parameters.size) / rect.width()); + const auto numPixelsToDraw = std::min(static_cast(parameters.size), rect.width()); - constexpr float maxFramesPerPixel = 512.0f; - const float resolution = std::max(1.0f, framesPerPixel / maxFramesPerPixel); - const float framesPerResolution = framesPerPixel / resolution; + const auto compFn = [](const SampleFrame& a, const SampleFrame& b) { return a.average() < b.average(); }; + const auto squaredSumFn = [](const float acc, const SampleFrame& x) { return acc + x.average() * x.average(); }; - size_t numPixels = std::min(parameters.size, static_cast(width)); - auto min = std::vector(numPixels, 1); - auto max = std::vector(numPixels, -1); - auto squared = std::vector(numPixels, 0); - - const size_t maxFrames = static_cast(numPixels * framesPerPixel); - - auto pixelIndex = std::size_t{0}; - - for (auto i = std::size_t{0}; i < maxFrames; i += static_cast(resolution)) + for (auto i = 0; i < numPixelsToDraw; i++) { - pixelIndex = i / framesPerPixel; - const auto frameIndex = !parameters.reversed ? i : maxFrames - i; + const auto start = parameters.buffer + static_cast(std::floor(i * samplesPerPixel)); + const auto end = parameters.buffer + static_cast(std::ceil((i + 1) * samplesPerPixel)); - const auto& frame = parameters.buffer[frameIndex]; - const auto value = frame.average(); + const auto [min, max] = std::minmax_element(start, end, compFn); + const auto minPeak = min->average(); + const auto maxPeak = max->average(); - if (value > max[pixelIndex]) { max[pixelIndex] = value; } - if (value < min[pixelIndex]) { min[pixelIndex] = value; } + const auto lineY1 = centerY - maxPeak * halfHeight * parameters.amplification; + const auto lineY2 = centerY - minPeak * halfHeight * parameters.amplification; + const auto lineX = rect.x() + (parameters.reversed ? numPixelsToDraw - i : i); - squared[pixelIndex] += value * value; - } - - if (pixelIndex < numPixels) - { - numPixels = pixelIndex; - } + const auto squaredSum = std::accumulate(start, end, 0.0f, squaredSumFn); + const auto rms = std::sqrt(squaredSum / samplesPerPixel); + const auto rmsLineY1 = centerY - rms * halfHeight * parameters.amplification; + const auto rmsLineY2 = centerY + rms * halfHeight * parameters.amplification; - for (auto i = std::size_t{0}; i < numPixels; i++) - { - const int lineY1 = centerY - max[i] * halfHeight * parameters.amplification; - const int lineY2 = centerY - min[i] * halfHeight * parameters.amplification; - const int lineX = static_cast(i) + x; painter.drawLine(lineX, lineY1, lineX, lineY2); - - 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 int rmsLineY1 = centerY - maxRMS * halfHeight * parameters.amplification; - const int rmsLineY2 = centerY - minRMS * halfHeight * parameters.amplification; - painter.setPen(rmsColor); painter.drawLine(lineX, rmsLineY1, lineX, rmsLineY2); painter.setPen(color);