diff --git a/include/Sample.h b/include/Sample.h index 2ccb78b19a0..102aaf2d515 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -96,7 +96,7 @@ class LMMS_EXPORT Sample auto sampleDuration() const -> std::chrono::milliseconds; auto sampleFile() const -> const QString& { return m_buffer->audioFile(); } auto sampleRate() const -> int { return m_buffer->sampleRate(); } - auto sampleSize() const -> int { return m_buffer->size(); } + auto sampleSize() const -> size_t { return m_buffer->size(); } auto toBase64() const -> QString { return m_buffer->toBase64(); } diff --git a/include/SampleWaveform.h b/include/SampleWaveform.h index 692c1f9cb37..0185e0e98f8 100644 --- a/include/SampleWaveform.h +++ b/include/SampleWaveform.h @@ -34,7 +34,15 @@ namespace lmms::gui { class LMMS_EXPORT SampleWaveform { public: - static void visualize(const Sample& sample, QPainter& p, const QRect& dr, int fromFrame = 0, int toFrame = 0); + struct Parameters + { + const sampleFrame* buffer; + size_t size; + float amplification; + bool reversed; + }; + + static void visualize(Parameters parameters, QPainter& painter, const QRect& rect); }; } // namespace lmms::gui diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index ce3c9c07b90..86b922fc421 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -719,7 +719,7 @@ void AudioFileProcessorWaveView::updateSampleRange() { const f_cnt_t marging = (m_sample->endFrame() - m_sample->startFrame()) * 0.1; m_from = qMax(0, m_sample->startFrame() - marging); - m_to = qMin(m_sample->endFrame() + marging, m_sample->sampleSize()); + m_to = qMin(m_sample->endFrame() + marging, m_sample->sampleSize()); } } @@ -1014,7 +1014,11 @@ void AudioFileProcessorWaveView::updateGraph() m_graph.fill( Qt::transparent ); QPainter p( &m_graph ); p.setPen( QColor( 255, 255, 255 ) ); - SampleWaveform::visualize(*m_sample, p, QRect(0, 0, m_graph.width(), m_graph.height()), m_from, m_to); + + const auto rect = QRect{0, 0, m_graph.width(), m_graph.height()}; + const auto waveform = SampleWaveform::Parameters{ + m_sample->data() + m_from, static_cast(m_to - m_from), m_sample->amplification(), m_sample->reversed()}; + SampleWaveform::visualize(waveform, p, rect); } @@ -1076,8 +1080,8 @@ void AudioFileProcessorWaveView::slide( int _px ) step = -step; } - f_cnt_t step_from = qBound(0, m_from + step, m_sample->sampleSize()) - m_from; - f_cnt_t step_to = qBound(m_from + 1, m_to + step, m_sample->sampleSize()) - m_to; + f_cnt_t step_from = qBound(0, m_from + step, m_sample->sampleSize()) - m_from; + f_cnt_t step_to = qBound(m_from + 1, m_to + step, m_sample->sampleSize()) - m_to; step = qAbs( step_from ) < qAbs( step_to ) ? step_from : step_to; diff --git a/plugins/SlicerT/SlicerTWaveform.cpp b/plugins/SlicerT/SlicerTWaveform.cpp index 66747036a1f..3793ed2f159 100644 --- a/plugins/SlicerT/SlicerTWaveform.cpp +++ b/plugins/SlicerT/SlicerTWaveform.cpp @@ -89,9 +89,10 @@ void SlicerTWaveform::drawSeekerWaveform() QPainter brush(&m_seekerWaveform); brush.setPen(s_waveformColor); - SampleWaveform::visualize(m_slicerTParent->m_originalSample, brush, - QRect(0, 0, m_seekerWaveform.width(), m_seekerWaveform.height()), 0, - m_slicerTParent->m_originalSample.sampleSize()); + const auto& sample = m_slicerTParent->m_originalSample; + const auto waveform = SampleWaveform::Parameters{sample.data(), sample.sampleSize(), sample.amplification(), sample.reversed()}; + const auto rect = QRect(0, 0, m_seekerWaveform.width(), m_seekerWaveform.height()); + SampleWaveform::visualize(waveform, brush, rect); // increase brightness in inner color QBitmap innerMask = m_seekerWaveform.createMaskFromColor(s_waveformMaskColor, Qt::MaskMode::MaskOutColor); @@ -139,14 +140,16 @@ void SlicerTWaveform::drawEditorWaveform() if (m_slicerTParent->m_originalSample.sampleSize() <= 1) { return; } QPainter brush(&m_editorWaveform); - float startFrame = m_seekerStart * m_slicerTParent->m_originalSample.sampleSize(); - float endFrame = m_seekerEnd * m_slicerTParent->m_originalSample.sampleSize(); + size_t startFrame = m_seekerStart * m_slicerTParent->m_originalSample.sampleSize(); + size_t endFrame = m_seekerEnd * m_slicerTParent->m_originalSample.sampleSize(); brush.setPen(s_waveformColor); float zoomOffset = (m_editorHeight - m_zoomLevel * m_editorHeight) / 2; - SampleWaveform::visualize(m_slicerTParent->m_originalSample, brush, - QRect(0, zoomOffset, m_editorWidth, m_zoomLevel * m_editorHeight), startFrame, endFrame); + const auto& sample = m_slicerTParent->m_originalSample; + const auto waveform = SampleWaveform::Parameters{sample.data() + startFrame, endFrame - startFrame, sample.amplification(), sample.reversed()}; + const auto rect = QRect(0, zoomOffset, m_editorWidth, m_zoomLevel * m_editorHeight); + SampleWaveform::visualize(waveform, brush, rect); // increase brightness in inner color QBitmap innerMask = m_editorWaveform.createMaskFromColor(s_waveformMaskColor, Qt::MaskMode::MaskOutColor); diff --git a/src/gui/SampleWaveform.cpp b/src/gui/SampleWaveform.cpp index 5d3afdee318..68b09b48271 100644 --- a/src/gui/SampleWaveform.cpp +++ b/src/gui/SampleWaveform.cpp @@ -26,31 +26,25 @@ namespace lmms::gui { -void SampleWaveform::visualize(const Sample& sample, QPainter& p, const QRect& dr, int fromFrame, int toFrame) +void SampleWaveform::visualize(Parameters parameters, QPainter& painter, const QRect& rect) { - if (sample.sampleSize() == 0) { return; } - - const auto x = dr.x(); - const auto height = dr.height(); - const auto width = dr.width(); - const auto centerY = dr.center().y(); + const auto x = rect.x(); + const auto height = rect.height(); + const auto width = rect.width(); + const auto centerY = rect.center().y(); const auto halfHeight = height / 2; - const auto buffer = sample.data() + fromFrame; - const auto color = p.pen().color(); + const auto color = painter.pen().color(); const auto rmsColor = color.lighter(123); - auto numFrames = toFrame - fromFrame; - if (numFrames == 0) { numFrames = sample.sampleSize(); } - - const auto framesPerPixel = std::max(1, numFrames / width); + const auto framesPerPixel = std::max(1, parameters.size / width); constexpr auto maxFramesPerPixel = 512; - const auto resolution = std::max(1, framesPerPixel / maxFramesPerPixel); + const auto resolution = std::max(1, framesPerPixel / maxFramesPerPixel); const auto framesPerResolution = framesPerPixel / resolution; - const auto numPixels = std::min(numFrames, width); + const auto numPixels = std::min(parameters.size, width); auto min = std::vector(numPixels, 1); auto max = std::vector(numPixels, -1); auto squared = std::vector(numPixels); @@ -59,35 +53,34 @@ void SampleWaveform::visualize(const Sample& sample, QPainter& p, const QRect& d for (int i = 0; i < maxFrames; i += resolution) { const auto pixelIndex = i / framesPerPixel; - const auto value = std::accumulate(buffer[i].begin(), buffer[i].end(), 0.0f) / buffer[i].size(); + const auto 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(); + if (value > max[pixelIndex]) { max[pixelIndex] = value; } if (value < min[pixelIndex]) { min[pixelIndex] = value; } + squared[pixelIndex] += value * value; } - const auto amplification = sample.amplification(); - const auto reversed = sample.reversed(); - for (int i = 0; i < numPixels; i++) { - const auto lineY1 = centerY - max[i] * halfHeight * amplification; - const auto lineY2 = centerY - min[i] * halfHeight * amplification; - - auto lineX = i + x; - if (reversed) { lineX = width - lineX; } - - p.drawLine(lineX, lineY1, lineX, lineY2); + const auto lineY1 = centerY - max[i] * halfHeight * parameters.amplification; + const auto lineY2 = centerY - min[i] * halfHeight * parameters.amplification; + const auto 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 auto rmsLineY1 = centerY - maxRMS * halfHeight * amplification; - const auto rmsLineY2 = centerY - minRMS * halfHeight * amplification; + const auto rmsLineY1 = centerY - maxRMS * halfHeight * parameters.amplification; + const auto rmsLineY2 = centerY - minRMS * halfHeight * parameters.amplification; - p.setPen(rmsColor); - p.drawLine(lineX, rmsLineY1, lineX, rmsLineY2); - p.setPen(color); + painter.setPen(rmsColor); + painter.drawLine(lineX, rmsLineY1, lineX, rmsLineY2); + painter.setPen(color); } } diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index 8f31633856f..ef46325e4e6 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -269,7 +269,10 @@ void SampleClipView::paintEvent( QPaintEvent * pe ) float offset = m_clip->startTimeOffset() / ticksPerBar * pixelsPerBar(); QRect r = QRect( offset, spacing, qMax( static_cast( m_clip->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); - SampleWaveform::visualize(m_clip->m_sample, p, r); + + const auto& sample = m_clip->m_sample; + const auto waveform = SampleWaveform::Parameters{sample.data(), sample.sampleSize(), sample.amplification(), sample.reversed()}; + SampleWaveform::visualize(waveform, p, r); QString name = PathUtil::cleanName(m_clip->m_sample.sampleFile()); paintTextLabel(name, p); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 90881c7efd0..c84725f44a7 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -1248,7 +1248,12 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) int yOffset = (editorHeight - sampleHeight) / 2.0f + TOP_MARGIN; p.setPen(m_ghostSampleColor); - SampleWaveform::visualize(m_ghostSample->sample(), p, QRect(startPos, yOffset, sampleWidth, sampleHeight), 0, sampleFrames); + + const auto& sample = m_ghostSample->sample(); + const auto waveform = SampleWaveform::Parameters{ + sample.data(), sample.sampleSize(), sample.amplification(), sample.reversed()}; + const auto rect = QRect(startPos, yOffset, sampleWidth, sampleHeight); + SampleWaveform::visualize(waveform, p, rect); } // draw ghost notes