Skip to content

feat: add real-time per-pedal pre/post spectrum analyzer#373

Open
Arbaaz123676 wants to merge 10 commits into
sudip-mondal-2002:mainfrom
Arbaaz123676:feat/pedal-spectrum-analyzer-clean
Open

feat: add real-time per-pedal pre/post spectrum analyzer#373
Arbaaz123676 wants to merge 10 commits into
sudip-mondal-2002:mainfrom
Arbaaz123676:feat/pedal-spectrum-analyzer-clean

Conversation

@Arbaaz123676

@Arbaaz123676 Arbaaz123676 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a real-time per-pedal pre/post spectrum analyzer, allowing users to visualize how individual pedals affect the incoming audio signal.

The implementation captures audio before and after pedal processing, performs FFT analysis using the existing spectrum analysis pipeline, and renders a lightweight floating overlay directly above the selected pedal.

Changes

  • Added pedal analyzer registration and unregistration APIs.
  • Added per-pedal pre/post signal capture infrastructure.
  • Integrated FFT processing using the existing SpectrumAnalyzer.
  • Added analyzer toggle control in the pedal header.
  • Added floating spectrum overlay with separate pre/post curves.
  • Enforced a maximum of 4 active analyzers to limit CPU overhead.
  • Applied project formatting rules (clang-format).

Technical Notes

  • Pre-processing and post-processing signals are captured independently.
  • FFT analysis is performed on captured pedal-specific audio data.
  • Existing spectrum analysis utilities are reused where possible.
  • Capture is enabled only for pedals with an active analyzer overlay.
  • Analyzer state is cleaned up automatically when closed or when a pedal is removed.

Validation

  • Project builds successfully.
  • Existing test suite passes (748/748).
  • Verified analyzer registration and cleanup flow.
  • Verified real-time capture and FFT processing path.
  • Confirmed only analyzer-related files are modified in this PR.

Issue

Fixes #73

Summary by CodeRabbit

  • New Features
    • Added per-pedal spectrum analyzer capture with independent “Pre [In]” and “Post [Out]” plots, updated from the latest captured FFT snapshot.
    • Added a “Spectrum analyzer” toggle in each pedal header to register/unregister analysis and refresh sequence-based rendering.
    • Exposed new pedal analyzer APIs for registration, sequence querying, and snapshot copying through the audio engine.
  • Bug Fixes
    • Ensured analyzer registration is cleaned up when a pedal widget is destroyed.
  • Tests
    • Added integration/unit tests for per-pedal analyzer registration, snapshot publishing/copying, audio engine API forwarding, and pedal widget analyzer rendering (plus minor UI test coverage).

@github-actions github-actions Bot added Audio Processing Related to DSP, effects, or signal processing GUI Related to the ImGui user interface and UX Hard level:advanced Advanced task · 45 GSSoC points type:feature New feature or request type:performance Performance optimization or latency improvement labels Jun 14, 2026
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds per-pedal pre/post spectrum analysis across audio engine and GUI layers. AnalyzerCapture gains a PedalCapture struct with ring buffers and atomic slot management for up to four nodes. AudioGraphExecutor calls capture_pedal per node. IAnalyzerProvider, AudioEngine, and their implementations forward registration/snapshot APIs. PedalWidget adds a toggle button and floating dB overlay with smoothed input/output curves. Comprehensive tests validate capture mechanics, snapshot publishing, widget lifecycle, and engine API forwarding.

Changes

Per-pedal Spectrum Analyzer

Layer / File(s) Summary
Interface contracts and executor signature
src/audio/engine/i_audio_engine.h, src/audio/engine/audio_graph_executor.h
IAnalyzerProvider adds four pure virtual pedal-analyzer methods (register, unregister, get sequence, copy snapshot); AudioGraphExecutor::process gains an optional forward-declared AnalyzerCapture* parameter with default nullptr.
PedalCapture data structure and AnalyzerCapture state
src/audio/engine/analyzer_capture.h
Introduces MAX_PEDAL_ANALYZERS constant, PedalCapture struct with per-node ring buffers/mutex-guarded snapshots/atomic sequence, and four-slot pedal_captures_ array; adds public and audio-thread method declarations for registration, sequence query, snapshot copy, and sample capture.
AnalyzerCapture pedal methods
src/audio/engine/analyzer_capture.cpp
Implements atomic CAS-based slot register/unregister, sequence query via slot lookup, mutex-guarded snapshot copy with nonzero-sequence validation, and ring-buffer sample writing with hop-threshold try-lock snapshot publishing and sequence increment.
Executor integration and AudioEngine API
src/audio/engine/audio_graph_executor.cpp, src/audio/engine/audio_graph_executor.h, src/audio/engine/audio_engine_process.cpp, src/audio/engine/audio_engine.h, src/audio/engine/audio_engine_api.cpp
Executor includes analyzer header, updates process signature to accept AnalyzerCapture*, and invokes capture_pedal per executed node; AudioEngine::process_audio passes analyzer_capture_.get() to executor; AudioEngine header declares and API layer implements the four pedal analyzer forwarding methods.
PedalWidget toggle button and spectrum overlay
src/gui/pedalboard/pedal_widget.h, src/gui/pedalboard/pedal_widget.cpp
Widget gains SpectrumAnalyzer instance, open flag, sequence counter, and FFT buffers; destructor unregisters analyzer on close; styled toggle button in pedal header registers/unregisters via engine and resets sequence; render path polls sequence, conditionally copies snapshot on change, updates analyzer, and draws floating dB grid overlay with smoothed Pre/Post curves and frequency tick labels.
Unit tests, UI tests, and test fixtures
tests/test_fixtures.h, tests/integration/test_audio_engine.cpp, tests/ui/test_pedal_widget.cpp, tests/ui/test_gui_keyboard_shortcuts.cpp, tests/ui/test_pedal_board_menu.cpp
Test fixtures reorder includes to apply macro redefinitions correctly; AnalyzerCapture registration, unregistration, snapshot publishing, and hop-threshold behavior validated; AudioEngine API forwarding verified; PedalWidget analyzer lifecycle, sample injection, rendering, and destructor cleanup exercised; minor formatting and additional test coverage for keyboard shortcuts.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(173, 216, 230, 0.5)
    Note over PedalWidget,AnalyzerCapture: Analyzer Registration
    PedalWidget->>AudioEngine: register_pedal_analyzer(node_id)
    AudioEngine->>AnalyzerCapture: register_pedal_analyzer(node_id)
    AnalyzerCapture-->>AudioEngine: slot claimed (bool)
    AudioEngine-->>PedalWidget: success
  end

  rect rgba(144, 238, 144, 0.5)
    Note over AudioGraphExecutor,AnalyzerCapture: Audio Thread Capture
    AudioGraphExecutor->>AnalyzerCapture: capture_pedal(node_id, input, output, count)
    AnalyzerCapture->>AnalyzerCapture: write ring buffer, accumulate hop count
    alt hop threshold reached
      AnalyzerCapture->>AnalyzerCapture: try_lock, copy snapshot, increment sequence
    end
  end

  rect rgba(255, 223, 128, 0.5)
    Note over PedalWidget,AnalyzerCapture: GUI Poll and Render
    PedalWidget->>AudioEngine: get_pedal_analyzer_sequence(node_id)
    AudioEngine->>AnalyzerCapture: get_pedal_analyzer_sequence
    AnalyzerCapture-->>PedalWidget: sequence counter
    alt new sequence available
      PedalWidget->>AudioEngine: copy_pedal_analyzer_snapshot(node_id, in_buf, out_buf)
      AudioEngine->>AnalyzerCapture: copy_pedal_analyzer_snapshot
      AnalyzerCapture-->>PedalWidget: FFT input/output buffers
    end
    PedalWidget->>PedalWidget: update SpectrumAnalyzer, draw Pre/Post overlay
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • sudip-mondal-2002/Amplitron#132: Introduces/refactors AudioGraphExecutor::process for DAG processing; this PR extends its signature to accept an AnalyzerCapture* parameter and integrate per-node capture calls.

Suggested labels

gssoc:approved, type:testing, quality:exceptional, High Impact

Poem

🐇 A bunny hops along the signal chain,
Watching frequencies rise and wane,
Blue curves in, green curves out,
Pre and Post — what FFT's about!
Four pedal slots, ring buffers tight,
The spectrum dances, day and night. 🎸

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive PR addresses core feature requirements but outstanding CI test failure and CodeRabbit warnings remain unresolved per comments, preventing full compliance verification. Resolve the failing CI test and address all CodeRabbit warnings before final assessment of linked issue compliance.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a real-time per-pedal spectrum analyzer feature with pre/post signal visualization.
Out of Scope Changes check ✅ Passed All changes directly support the per-pedal spectrum analyzer feature: audio capture infrastructure, GUI integration, analyzer management APIs, and related tests with only minor formatting adjustments to test files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.58011% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/gui/pedalboard/pedal_widget.cpp 91.11% 8 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

PR Preview Ready

Preview URL: https://amplitron.sudipmondal.co.in/pr-previews/pr-373/

Built from commit a9e46f9 and deployed to pr-previews/pr-373.
This preview updates automatically when the PR branch changes and is removed when the PR closes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/audio/engine/analyzer_capture.h (1)

80-80: 💤 Low value

Consider extracting the magic number 4 to a named constant.

The array size 4 is hardcoded here and repeated in multiple methods throughout analyzer_capture.cpp (lines 89, 96, 110, 121, 134, 155). Extracting this to a named constant would improve maintainability.

Suggested improvement
 class AnalyzerCapture : public IAnalyzerProvider {
    public:
     static constexpr int ANALYZER_FFT_SIZE = 2048;
     static constexpr int ANALYZER_FFT_MASK = ANALYZER_FFT_SIZE - 1;
     static constexpr int ANALYZER_HOP_SIZE = 1024;
+    static constexpr int MAX_PEDAL_ANALYZERS = 4;

Then use MAX_PEDAL_ANALYZERS in the array declaration and loop bounds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/audio/engine/analyzer_capture.h` at line 80, Extract the hardcoded magic
number 4 to a named constant such as MAX_PEDAL_ANALYZERS defined in the
analyzer_capture.h file (this could be a constexpr static member or a file-level
constant). Replace the hardcoded 4 in the pedal_captures_ array declaration with
this constant, and then identify and replace all other occurrences of the
hardcoded 4 used as loop bounds and array sizes throughout analyzer_capture.cpp
(which appear in multiple methods) with this same constant to ensure consistency
and maintainability across the codebase.
src/gui/pedalboard/pedal_widget.cpp (1)

173-260: ⚡ Quick win

Consider extracting overlay rendering to a helper method.

The spectrum analyzer overlay rendering logic (88 lines) could be extracted into a private render_spectrum_overlay() helper method to improve readability and modularity. This would also make the main render() method easier to follow.

♻️ Proposed refactor outline

Add a private helper method in the header:

void render_spectrum_overlay(ImDrawList* dl, ImVec2 pedal_pos, float pedal_width, float zoom);

Then extract lines 174-260 into this method, passing the necessary context. The main render() would simply call:

if (analyzer_open_) {
    render_spectrum_overlay(dl, p0, pedal_width, zoom);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/gui/pedalboard/pedal_widget.cpp` around lines 173 - 260, Extract the
spectrum analyzer overlay rendering logic (lines 173-260) from the main render
method into a new private helper method called render_spectrum_overlay() that
accepts ImDrawList* dl, ImVec2 pedal_pos, float pedal_width, and float zoom as
parameters. Move all the code from the if (analyzer_open_) block including the
spectrum data polling, overlay background drawing, reference dB line rendering,
frequency tick marking, curve rendering with the draw_curve lambda, and legend
labeling into this new helper method. Then replace the entire if
(analyzer_open_) block in the main render() method with a single call to
render_spectrum_overlay(dl, p0, pedal_width, zoom), ensuring that all member
variables accessed in the extracted code remain accessible.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/audio/engine/analyzer_capture.h`:
- Line 58: Rename the class member variable `node_id` to `node_id_` in the
struct definition to comply with the coding guidelines requiring a trailing
underscore for class member variables. After renaming the variable declaration,
update all references to `node_id` throughout the corresponding implementation
file to use the new name `node_id_`, ensuring consistency across all usages of
this member variable.
- Around line 69-78: The `reset()` method has a critical data race on
`capture_index_` and `samples_since_publish_` because these non-atomic variables
are written by both the GUI thread (in `reset()`) and the audio thread (in
`capture_pedal`), with insufficient synchronization. In
`register_pedal_analyzer`, move the `reset()` call to execute before the
compare-and-swap operation that assigns the node_id to the slot, ensuring the
audio thread never observes an uninitialized state. In
`unregister_pedal_analyzer`, after the CAS clears the node_id, the audio thread
may still be executing in the capture block and will race with the subsequent
`reset()` call; either move `reset()` to before the CAS operation as well, or
consider making `capture_index_` and `samples_since_publish_` atomic variables
to eliminate the synchronization dependency on the mutex.

---

Nitpick comments:
In `@src/audio/engine/analyzer_capture.h`:
- Line 80: Extract the hardcoded magic number 4 to a named constant such as
MAX_PEDAL_ANALYZERS defined in the analyzer_capture.h file (this could be a
constexpr static member or a file-level constant). Replace the hardcoded 4 in
the pedal_captures_ array declaration with this constant, and then identify and
replace all other occurrences of the hardcoded 4 used as loop bounds and array
sizes throughout analyzer_capture.cpp (which appear in multiple methods) with
this same constant to ensure consistency and maintainability across the
codebase.

In `@src/gui/pedalboard/pedal_widget.cpp`:
- Around line 173-260: Extract the spectrum analyzer overlay rendering logic
(lines 173-260) from the main render method into a new private helper method
called render_spectrum_overlay() that accepts ImDrawList* dl, ImVec2 pedal_pos,
float pedal_width, and float zoom as parameters. Move all the code from the if
(analyzer_open_) block including the spectrum data polling, overlay background
drawing, reference dB line rendering, frequency tick marking, curve rendering
with the draw_curve lambda, and legend labeling into this new helper method.
Then replace the entire if (analyzer_open_) block in the main render() method
with a single call to render_spectrum_overlay(dl, p0, pedal_width, zoom),
ensuring that all member variables accessed in the extracted code remain
accessible.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 897e22d2-b7c8-4522-b519-e4e5385e9203

📥 Commits

Reviewing files that changed from the base of the PR and between 7a09319 and 2abdfbe.

📒 Files selected for processing (10)
  • src/audio/engine/analyzer_capture.cpp
  • src/audio/engine/analyzer_capture.h
  • src/audio/engine/audio_engine.h
  • src/audio/engine/audio_engine_api.cpp
  • src/audio/engine/audio_engine_process.cpp
  • src/audio/engine/audio_graph_executor.cpp
  • src/audio/engine/audio_graph_executor.h
  • src/audio/engine/i_audio_engine.h
  • src/gui/pedalboard/pedal_widget.cpp
  • src/gui/pedalboard/pedal_widget.h

Comment thread src/audio/engine/analyzer_capture.h Outdated
Comment thread src/audio/engine/analyzer_capture.h
@MohitBareja16

Copy link
Copy Markdown
Collaborator

@Arbaaz123676 CI test failing

@MohitBareja16

Copy link
Copy Markdown
Collaborator

@Arbaaz123676 Check coderabbit warnings and push the test coverage

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integration/test_audio_engine.cpp`:
- Around line 548-552: The GetterTestCoverage test contains tautological
assertions that are always true and provide no real validation. Replace the
size() >= 0u assertions for test_process_buffer() and
test_process_buffer_right() with meaningful checks such as verifying the buffers
contain expected data or have specific properties. Replace the
test_audio_shadow_executor() assertion (which checks ptr == nullptr || ptr !=
nullptr) with a real postcondition that validates the executor's actual state or
properties, such as asserting it is or is not null based on expected behavior,
or checking specific attributes of the executor object.

In `@tests/ui/test_pedal_widget.cpp`:
- Around line 170-190: The test ignores the return value of engine.initialize()
but later dereferences engine.analyzer_capture_ on line 189 through the
capture_pedal call, which could be null if initialization failed. Add an
assertion or check immediately after engine.initialize() to verify that
initialization succeeded before proceeding with any code that accesses
engine.analyzer_capture_ and its methods like capture_pedal. This ensures the
test will not crash or flap due to uninitialized state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac5f70b4-e335-4211-a3c8-c8d693a5699d

📥 Commits

Reviewing files that changed from the base of the PR and between f1405fa and 84e45e3.

📒 Files selected for processing (5)
  • tests/integration/test_audio_engine.cpp
  • tests/test_fixtures.h
  • tests/ui/test_gui_keyboard_shortcuts.cpp
  • tests/ui/test_pedal_board_menu.cpp
  • tests/ui/test_pedal_widget.cpp
✅ Files skipped from review due to trivial changes (1)
  • tests/ui/test_pedal_board_menu.cpp

Comment thread tests/integration/test_audio_engine.cpp
Comment thread tests/ui/test_pedal_widget.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/audio/engine/analyzer_capture.cpp`:
- Around line 96-104: The reset() method on pedal_captures_[i] is called before
the compare_exchange_strong operation succeeds, creating a race condition where
another thread could claim the slot between the initial load check and the
reset, causing data corruption. Move the reset() call to execute only after the
compare_exchange_strong succeeds by placing it inside the if block that verifies
the CAS operation was successful, ensuring we only reset slots that we have
actually claimed.
- Around line 155-166: The capture_pedal method is missing input validation that
could lead to undefined behavior when called from the audio thread. Add
null-pointer validation for the input and output parameters and bounds
validation for the count parameter (ensure count is positive) at the start of
the capture_pedal method, before the loop begins. Follow the same validation
pattern used in other analyzer methods like copy_analyzer_snapshot and
copy_pedal_analyzer_snapshot in this file to ensure consistency.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71d96b5f-0523-4cad-af9b-ec951e640d89

📥 Commits

Reviewing files that changed from the base of the PR and between 84e45e3 and 1e2e859.

📒 Files selected for processing (7)
  • src/audio/engine/analyzer_capture.cpp
  • src/audio/engine/analyzer_capture.h
  • src/gui/pedalboard/pedal_widget.cpp
  • src/gui/pedalboard/pedal_widget.h
  • tests/integration/test_audio_engine.cpp
  • tests/test_fixtures.h
  • tests/ui/test_pedal_widget.cpp
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/test_fixtures.h
  • tests/ui/test_pedal_widget.cpp
  • tests/integration/test_audio_engine.cpp
  • src/gui/pedalboard/pedal_widget.h
  • src/gui/pedalboard/pedal_widget.cpp
  • src/audio/engine/analyzer_capture.h

Comment thread src/audio/engine/analyzer_capture.cpp
Comment thread src/audio/engine/analyzer_capture.cpp
@sudip-mondal-2002

Copy link
Copy Markdown
Owner

@Arbaaz123676 Can you explain how to use it? Please attach a video recording

@Arbaaz123676

Copy link
Copy Markdown
Contributor Author

@sudip-mondal-2002

Real.time.per.pedal.spectrum.analysis.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Audio Processing Related to DSP, effects, or signal processing GUI Related to the ImGui user interface and UX Hard level:advanced Advanced task · 45 GSSoC points type:feature New feature or request type:performance Performance optimization or latency improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add per-pedal pre/post spectrum analyser to visualise the effect of each pedal

3 participants