diff --git a/plugins/SpectrumAnalyzer/SaProcessor.cpp b/plugins/SpectrumAnalyzer/SaProcessor.cpp index 3fa6c2048df..7a86731cf9d 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.cpp +++ b/plugins/SpectrumAnalyzer/SaProcessor.cpp @@ -225,12 +225,9 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) if (band_end - band_start > 1.0) { // band spans multiple pixels: draw all pixels it covers - for (target = (int)band_start; target < (int)band_end; target++) + for (target = std::max((int)band_start, 0); target < band_end && target < waterfallWidth(); target++) { - if (target >= 0 && target < waterfallWidth()) - { - pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); - } + pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); } // save remaining portion of the band for the following band / pixel // (in case the next band uses sub-pixel drawing) @@ -265,12 +262,9 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) else { // Linear: always draws one or more pixels per band - for (target = (int)band_start; target < band_end; target++) + for (target = std::max((int)band_start, 0); target < band_end && target < waterfallWidth(); target++) { - if (target >= 0 && target < waterfallWidth()) - { - pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); - } + pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); } } } @@ -573,6 +567,9 @@ float SaProcessor::getFreqRangeMax() const // Map frequency to pixel x position on a display of given width. +// NOTE: Results of this function may be cached by SaSpectrumView. If you use +// a new function call or variable that can affect results of this function, +// make sure to also add it as a trigger for cache update in SaSpectrumView. float SaProcessor::freqToXPixel(float freq, unsigned int width) const { if (m_controls->m_logXModel.value()) diff --git a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp index 849cba66540..cf584431d81 100644 --- a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp +++ b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp @@ -47,7 +47,13 @@ SaSpectrumView::SaSpectrumView(SaControls *controls, SaProcessor *processor, QWi m_controls(controls), m_processor(processor), m_freezeRequest(false), - m_frozen(false) + m_frozen(false), + m_cachedRangeMin(-1), + m_cachedRangeMax(-1), + m_cachedLogX(true), + m_cachedDisplayWidth(0), + m_cachedBinCount(0), + m_cachedSampleRate(0) { setMinimumSize(360, 170); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); @@ -69,6 +75,9 @@ SaSpectrumView::SaSpectrumView(SaControls *controls, SaProcessor *processor, QWi m_cursor = QPointF(0, 0); + // Initialize the size of bin → pixel X position LUT to the maximum allowed number of bins + 1. + m_cachedBinToX.resize(FFT_BLOCK_SIZES.back() / 2 + 2); + #ifdef SA_DEBUG m_execution_avg = m_path_avg = m_draw_avg = 0; #endif @@ -348,10 +357,31 @@ QPainterPath SaSpectrumView::makePath(std::vector &displayBuffer, float r // Bins falling to interval [x_start, x_next) contribute to a single point. float max = m_displayBottom; float x_start = -1; // lower bound of currently constructed point + + // Speed up bin → x position translation by building a LUT cache. + // Update the cache only when range or display width are changed. + float rangeMin = m_processor->getFreqRangeMin(m_controls->m_logXModel.value()); + float rangeMax = m_processor->getFreqRangeMax(); + if (rangeMin != m_cachedRangeMin || rangeMax != m_cachedRangeMax || m_displayWidth != m_cachedDisplayWidth || + m_controls->m_logXModel.value() != m_cachedLogX || m_processor->binCount() + 1 != m_cachedBinCount || + m_processor->getSampleRate() != m_cachedSampleRate) + { + m_cachedRangeMin = rangeMin; + m_cachedRangeMax = rangeMax; + m_cachedDisplayWidth = m_displayWidth; + m_cachedLogX = m_controls->m_logXModel.value(); + m_cachedBinCount = m_processor->binCount() + 1; + m_cachedSampleRate = m_processor->getSampleRate(); + for (unsigned int n = 0; n < m_cachedBinCount; n++) + { + m_cachedBinToX[n] = freqToXPixel(binToFreq(n), m_displayWidth); + } + } + for (unsigned int n = 0; n < m_processor->binCount(); n++) { - float x = freqToXPixel(binToFreq(n), m_displayWidth); - float x_next = freqToXPixel(binToFreq(n + 1), m_displayWidth); + float x = m_cachedBinToX[n]; + float x_next = m_cachedBinToX[n + 1]; float y = ampToYPixel(displayBuffer[n], m_displayBottom); // consider making a point only if x falls within display bounds diff --git a/plugins/SpectrumAnalyzer/SaSpectrumView.h b/plugins/SpectrumAnalyzer/SaSpectrumView.h index b59264d9ce7..0894cd383d0 100644 --- a/plugins/SpectrumAnalyzer/SaSpectrumView.h +++ b/plugins/SpectrumAnalyzer/SaSpectrumView.h @@ -31,6 +31,7 @@ #include #include +#include #include #include @@ -120,6 +121,15 @@ private slots: unsigned int m_displayRight; unsigned int m_displayWidth; + // cached frequency bin → x position conversion for better performance + std::vector m_cachedBinToX; + float m_cachedRangeMin; + float m_cachedRangeMax; + bool m_cachedLogX; + unsigned int m_cachedDisplayWidth; + unsigned int m_cachedBinCount; + unsigned int m_cachedSampleRate; + #ifdef SA_DEBUG float m_execution_avg; float m_refresh_avg;