diff --git a/src/ui/Application.cpp b/src/ui/Application.cpp index d525a97..1ef430f 100644 --- a/src/ui/Application.cpp +++ b/src/ui/Application.cpp @@ -702,6 +702,7 @@ void Application::loadConfig() { cursors_.snapToPeaks = config_.getBool("snap_to_peaks", cursors_.snapToPeaks); measurements_.traceMinFreq = config_.getFloat("trace_min_freq", measurements_.traceMinFreq); measurements_.traceMaxFreq = config_.getFloat("trace_max_freq", measurements_.traceMaxFreq); + ui_.specMinPixPerBin = config_.getInt("spec_min_pix_per_bin", ui_.specMinPixPerBin); // Clamp controlPanel_.fftSizeIdx = std::clamp(controlPanel_.fftSizeIdx, 0, ControlPanel::kNumFFTSizes - 1); @@ -766,6 +767,7 @@ void Application::saveConfig() const { cfg.setBool("snap_to_peaks", cursors_.snapToPeaks); cfg.setFloat("trace_min_freq", measurements_.traceMinFreq); cfg.setFloat("trace_max_freq", measurements_.traceMaxFreq); + cfg.setInt("spec_min_pix_per_bin", ui_.specMinPixPerBin); int devIdx = audio_.deviceIdx(); if (devIdx >= 0 && devIdx < static_cast(devices.size())) diff --git a/src/ui/ControlPanel.cpp b/src/ui/ControlPanel.cpp index 6a54d2c..f055126 100644 --- a/src/ui/ControlPanel.cpp +++ b/src/ui/ControlPanel.cpp @@ -178,6 +178,15 @@ void ControlPanel::render(AudioEngine& audio, UIState& ui, } if (ImGui::IsItemHovered()) ImGui::SetTooltip("Reset to 2x zoom"); } + + { + ImGui::AlignTextToFramePadding(); + ImGui::Text("Decim:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); + ImGui::SliderInt("##decimation", &ui.specMinPixPerBin, 1, 8, "%d px/bin"); + if (ImGui::IsItemHovered()) ImGui::SetTooltip("Spectrum frequency decimation (peak-detect)"); + } } // ── Channels ── diff --git a/src/ui/DisplayPanel.cpp b/src/ui/DisplayPanel.cpp index 65a3484..5d2c54a 100644 --- a/src/ui/DisplayPanel.cpp +++ b/src/ui/DisplayPanel.cpp @@ -86,7 +86,7 @@ void DisplayPanel::renderSpectrum(AudioEngine& audio, UIState& ui, specDisplay.draw(allSpectraScratch_, stylesScratch_, ui.minDB, ui.maxDB, settings.sampleRate, settings.isIQ, ui.freqScale, specPosX, specPosY, specSizeX, specSizeY, - ui.viewLo, ui.viewHi); + ui.viewLo, ui.viewHi, ui.specMinPixPerBin); cursors.draw(specDisplay, specPosX, specPosY, specSizeX, specSizeY, settings.sampleRate, settings.isIQ, ui.freqScale, ui.minDB, ui.maxDB, diff --git a/src/ui/SpectrumDisplay.cpp b/src/ui/SpectrumDisplay.cpp index 007313e..30abe7e 100644 --- a/src/ui/SpectrumDisplay.cpp +++ b/src/ui/SpectrumDisplay.cpp @@ -60,9 +60,11 @@ static void buildPolyline(const std::vector& spectrumDB, bool isIQ, FreqScale freqScale, float posX, float posY, float sizeX, float sizeY, float viewLo, float viewHi, - std::vector& outPoints) { + std::vector& outPoints, + int minPixPerBin = 1) { int bins = static_cast(spectrumDB.size()); - int displayPts = std::min(bins, static_cast(sizeX)); + int maxPts = std::max(1, static_cast(sizeX) / std::max(1, minPixPerBin)); + int displayPts = std::min(bins, maxPts); if (displayPts < 2) displayPts = 2; outPoints.resize(displayPts); @@ -108,7 +110,8 @@ void SpectrumDisplay::draw(const std::vector>& spectra, FreqScale freqScale, float posX, float posY, float sizeX, float sizeY, - float viewLo, float viewHi) const { + float viewLo, float viewHi, + int minPixPerBin) const { if (spectra.empty() || spectra[0].empty() || sizeX <= 0 || sizeY <= 0) return; ImDrawList* dl = ImGui::GetWindowDrawList(); @@ -178,7 +181,7 @@ void SpectrumDisplay::draw(const std::vector>& spectra, if (spectra[ch].empty()) continue; buildPolyline(spectra[ch], minDB, maxDB, isIQ, freqScale, posX, posY, sizeX, sizeY, - viewLo, viewHi, allPoints[ch]); + viewLo, viewHi, allPoints[ch], minPixPerBin); } // Draw spectra: all fills first, then all lines on top. @@ -237,7 +240,7 @@ void SpectrumDisplay::draw(const std::vector>& spectra, ImU32 col = (st.lineColor & 0x00FFFFFF) | 0x90000000; buildPolyline(peakHold_[ch], minDB, maxDB, isIQ, freqScale, posX, posY, sizeX, sizeY, - viewLo, viewHi, phPoints); + viewLo, viewHi, phPoints, minPixPerBin); if (phPoints.size() >= 2) dl->AddPolyline(phPoints.data(), static_cast(phPoints.size()), col, ImDrawFlags_None, 1.0f); diff --git a/src/ui/SpectrumDisplay.h b/src/ui/SpectrumDisplay.h index 7e9ebce..4072738 100644 --- a/src/ui/SpectrumDisplay.h +++ b/src/ui/SpectrumDisplay.h @@ -22,7 +22,8 @@ public: double sampleRate, bool isIQ, FreqScale freqScale, float posX, float posY, float sizeX, float sizeY, - float viewLo = 0.0f, float viewHi = 1.0f) const; + float viewLo = 0.0f, float viewHi = 1.0f, + int minPixPerBin = 1) const; // Convenience: single-channel draw (backward compat). void draw(const std::vector& spectrumDB, diff --git a/src/ui/UIState.h b/src/ui/UIState.h index f656860..2b76893 100644 --- a/src/ui/UIState.h +++ b/src/ui/UIState.h @@ -17,6 +17,7 @@ struct UIState { int waterfallChannel = 0; bool waterfallMultiCh = true; + int specMinPixPerBin = 1; // min screen pixels per spectrum bin (1 = no decimation) std::array channelEnabled = {true,true,true,true,true,true,true,true}; // Complementary pairs: colors at indices 0+1, 2+3, 4+5, 6+7 sum to white. std::array channelColors = {{