Skip to content

feat: implement N-input dynamic mixer with per-channel gain routing#277

Merged
MohitBareja16 merged 10 commits into
sudip-mondal-2002:mainfrom
MohitBareja16:feature/n-input-mixer
May 30, 2026
Merged

feat: implement N-input dynamic mixer with per-channel gain routing#277
MohitBareja16 merged 10 commits into
sudip-mondal-2002:mainfrom
MohitBareja16:feature/n-input-mixer

Conversation

@MohitBareja16

@MohitBareja16 MohitBareja16 commented May 28, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

This PR upgrades the existing 2-input Mixer node into a fully dynamic N-input Mixer (supporting 2 to 8 input pins). It allows users to build complex parallel routing topologies, such as blending a clean amp, a distorted amp, and a direct signal simultaneously. It also introduces independent, MIDI-learnable gain faders for each channel, fully integrated with the lock-free SPSC command queue to ensure glitch-free updates on the real-time audio thread.

Related Issue

Fixes #147

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • ♻️ Refactor / code cleanup
  • 📝 Documentation
  • ✅ Tests
  • 🔧 Build / CI / Configuration

How Was This Tested?

  • Platform(s) tested on: Linux (Ubuntu)
  • Test command run: make && ./build/amplitron-tests
  • Manual test steps (if applicable):
    1. Opened the GUI and spawned an N-in Mixer from the creation menu.
    2. Right-clicked the mixer to verify dynamic addition and removal of input pins via the context menu.
    3. Verified that pin removal is correctly blocked when an active cable is connected to it.
    4. Adjusted the newly labeled (Gain Fader) sliders while passing audio to verify zero-glitch volume scaling.
    5. Mapped a MIDI controller to the gain faders and verified real-time manipulation.
    6. Saved and loaded a preset to ensure dynamic pin counts and gain values persist correctly.

Checklist

  • Code compiles and builds successfully on my platform
  • All existing tests pass (./amplitron-tests)
  • New tests added for new functionality (if applicable)
  • Documentation updated (README, code comments) if applicable
  • No blocking calls on the audio thread (no std::mutex::lock() on the hot path)
  • Tested on: Linux

Screenshots / Demo

Screenshot from 2026-05-28 21-39-35

Summary by CodeRabbit

  • New Features

    • Mixers support dynamic N-inputs (2–8) with per-input gain sliders, MIDI mapping, and menu label updates.
    • Splitters support dynamic N-outputs.
    • UI: smoother canvas zoom/scroll (cursor-anchored zoom), fullscreen toggle, optional grid, animated visuals and improved node layout/drag.
  • Behavior

    • Pins auto-added/removed when wiring changes with undo/redo restoring pins; gains clamped and applied in real time.
  • Tests

    • Added integration tests for multi-input mixers and per-input gains.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@MohitBareja16, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 48 minutes and 34 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e457cf07-0069-4af8-81a2-8aa694d22ad2

📥 Commits

Reviewing files that changed from the base of the PR and between 7d9c1c5 and 5f61709.

📒 Files selected for processing (4)
  • src/gui/commands/command_graph.h
  • src/gui/pedalboard/pedal_board_chain.cpp
  • src/gui/pedalboard/pedal_widget.cpp
  • src/gui/pedalboard/pedal_widget_body.cpp
📝 Walkthrough

Walkthrough

Adds dynamic 2–8 input mixer support with per-input gains: graph stores per-pin gains and exposes pin add/remove/restore and gain APIs; executor compiles/apply weighted sums and supports runtime gain updates; engine enqueues SetMixerGain commands; GUI exposes pin management, gain sliders (MIDI-mappable), and integration tests validate behavior.

Changes

N-Input Mixer Node Implementation

Layer / File(s) Summary
Audio graph mixer data model
src/audio/engine/audio_graph.h, src/audio/engine/audio_graph.cpp
DSPNode gains input_gains vector initialized per mixer input count; AudioGraph::add_input_pin(), remove_input_pin(), restore_input_pin(), and set_mixer_input_gain() manage dynamic pins with bounds and link checks.
Audio execution gain application
src/audio/engine/audio_graph_executor.h, src/audio/engine/audio_graph_executor.cpp
InputSource carries per-connection gain and pin_index; compile() records per-pin gains from graph state; process() applies weighted input summation with a unity fast path; update_mixer_gain() updates execution_plan_ entries at runtime.
Engine command infrastructure for mixer gain changes
src/audio/utils/spsc_queue.h, src/audio/engine/audio_engine.h, src/audio/engine/audio_engine_api.cpp, src/audio/engine/audio_engine_process.cpp
AudioCommand::Type adds SetMixerGain; AudioEngine::push_mixer_gain_change() enqueues a SetMixerGain command (embedding node/pin in command fields); drain_gain_commands() forwards SetMixerGain to the executor while the mutex-gated drain_commands() no-ops SetMixerGain.
Graph command undo/redo pin management
src/gui/commands/command_graph.h
AddGraphNodeCommand accepts num_inputs; add/remove link/node commands cache and restore auto-added/auto-removed mixer input pin state for undo/redo.
Mixer node UI rendering and pin/gain interaction
src/gui/pedalboard/pedal_board_chain.cpp
Pedalboard node rendering shows dynamic channel count, right-click add/remove pin actions, per-input gain sliders that call audio_graph.set_mixer_input_gain() and engine.push_mixer_gain_change(), MIDI mapping popups, and preserved cable/wire interactions and visuals.
Mixer menu creation and MIDI gain mapping
src/gui/pedalboard/pedal_board_menu.cpp, src/midi/midi_manager_mapping.cpp
Menu label updated to “N-In -> 1 Out”; MIDI mapping parses Mixer_<id> and Gain <n> to set graph gain and push engine gain change.
Integration tests and file cleanup
tests/integration/*, tests/ui/*
Adds audio_graph_mixer_n_inputs and audio_graph_mixer_gains tests; updates include paths to presets/; reformats serialization and UI tests and adds small test helpers.

Sequence Diagram(s)

sequenceDiagram
  participant GUI as PedalBoard UI
  participant Engine as AudioEngine
  participant Executor as AudioGraphExecutor
  GUI->>Engine: push_mixer_gain_change(node_id,pin_index,gain)
  Engine->>Executor: drain_gain_commands() forwards SetMixerGain -> update_mixer_gain(node_id,pin_index,gain)
  Executor->>Executor: update execution_plan_.input_sources[].gain
  Executor->>Executor: process() applies gain * input_buffer (unity fast-path when gain==1.0)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

GUI, type:testing

Suggested reviewers

  • sudip-mondal-2002

"🐇 I twiddle sliders, nose in the breeze,
N inputs hum, each gain with ease,
MIDI learns the gentle law,
Executor sums without a flaw,
Hopping home — the mix is pleased."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: implement N-input dynamic mixer with per-channel gain routing' accurately captures the main change—extending the mixer to support dynamic N-input pins with independent per-channel gain control.
Linked Issues check ✅ Passed All coding requirements from #147 are met: N-input pins (2–8) with configurable defaults [audio_graph.h/cpp], per-input gain faders (0.0–2.0) stored in input_gains [audio_graph.h], MIDI-learnable mapping in midi_manager_mapping.cpp, add/remove input pin APIs [audio_graph.h/cpp], correct weighted-sum mixing in AudioGraphExecutor [audio_graph_executor.cpp/h], lock-free gain updates via SetMixerGain command [spsc_queue.h, audio_engine*.cpp], and tests verifying 3/4-input mixes, gains, and pin removal [test_audio_graph.cpp].
Out of Scope Changes check ✅ Passed All changes align with the N-input mixer feature scope. AudioEngine API, AudioGraph, command queue, executor, gain routing, UI rendering, menu updates, MIDI mapping, and integration tests are all directly related to implementing dynamic mixer pins and per-channel gain control.

✏️ 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.

@github-actions github-actions Bot added Audio Processing Related to DSP, effects, or signal processing DAG Related to DAG-based modular signal routing architecture High Impact Significant user-facing improvement level:advanced Advanced task · 45 GSSoC points type:feature New feature or request labels May 28, 2026
@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

🌍 Cross-Platform Coverage Report

Platforms: Linux + macOS + Windows
Merged Line Coverage: 78.0%

✅ Coverage meets threshold: 78.0% >= 60%

Full Merged Coverage Summary
Summary coverage rate:
  lines......: 78.0% (8423 of 10796 lines)
  functions..: 92.6% (861 of 930 functions)
  branches...: no data found

@github-actions

github-actions Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

PR Preview Removed

The GitHub Pages preview for this PR has been removed because the PR was closed.

@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: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/audio/engine/audio_graph.cpp (1)

17-23: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Clamp mixer input count at creation time.

Line 18 currently accepts any positive num_inputs, which allows creating mixers outside the 2–8 contract.

Proposed fix
-    int inputs_to_create = (num_inputs > 0) ? num_inputs : 2;
+    const int requested_inputs = (num_inputs > 0) ? num_inputs : 2;
+    const int inputs_to_create = std::clamp(requested_inputs, 2, 8);
🤖 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/audio_graph.cpp` around lines 17 - 23, The mixer/merge-sum
creation uses num_inputs unchecked, allowing values outside the 2–8 contract;
change the calculation of inputs_to_create in the NodeRoutingType::Mixer and
NodeRoutingType::MergeSum branch to clamp num_inputs into the range [2,8] (e.g.
use std::clamp or manual min/max) before allocating input_pin_ids, input_gains
and assigning output_pin_ids; ensure the rest of the block
(node.input_pin_ids.push_back(next_id_++), node.input_gains.assign(...),
node.output_pin_ids.push_back(...)) uses this clamped inputs_to_create value.
🧹 Nitpick comments (2)
src/gui/pedalboard/pedal_board_chain.cpp (2)

343-347: 💤 Low value

Empty if blocks can be simplified.

The return values from render_remove_mapping_item and render_learn_menu_item are checked but not used. Either remove the conditionals or add a comment if the return values will be used later.

♻️ Suggested simplification
           if (gui_midi_) {
-              if (gui_midi_->render_remove_mapping_item(effect_name,
-                                                        param_name)) {
-              }
-              if (gui_midi_->render_learn_menu_item(effect_name, param_name)) {
-              }
+              gui_midi_->render_remove_mapping_item(effect_name, param_name);
+              gui_midi_->render_learn_menu_item(effect_name, param_name);
🤖 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_board_chain.cpp` around lines 343 - 347, The two
empty conditionals around gui_midi_->render_remove_mapping_item(effect_name,
param_name) and gui_midi_->render_learn_menu_item(effect_name, param_name)
should be simplified: remove the surrounding if(...) { } wrappers and call the
methods directly (or if the boolean return is intentionally significant, replace
the empty bodies with a brief comment like // return value intentionally
ignored). Update the calls to gui_midi_->render_remove_mapping_item(effect_name,
param_name) and gui_midi_->render_learn_menu_item(effect_name, param_name)
accordingly to eliminate the empty if blocks.

546-547: ⚡ Quick win

Remove debug printf statement.

This debug output will spam the console during normal wire drag-and-drop operations and should be removed before merging.

🧹 Remove debug code
         if (dist_sq < pow(15.0f * ui_state.zoom, 2) &&
             ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
-          printf("MANUAL DROP HOVER DETECTED! src: %d, dest: %d\n",
-                 ui_state.active_src_pin_id, pin_id);
           if (ui_state.active_src_pin_id != -1) {
             history_.execute(std::make_unique<AddGraphLinkCommand>(
                 engine_, ui_state.active_src_pin_id, pin_id));
🤖 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_board_chain.cpp` around lines 546 - 547, Remove the
debug printf that prints "MANUAL DROP HOVER DETECTED!" — delete the printf call
that references ui_state.active_src_pin_id and pin_id in pedal_board_chain.cpp
(the manual drop hover detection block) so it no longer spams the console; if
you want to keep conditional logging, replace it with a proper debug/log macro
(e.g., LOG_DEBUG) guarded by the project's debug build flag instead of leaving
the raw printf.
🤖 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/audio_engine_process.cpp`:
- Around line 294-299: The SetMixerGain command is currently processed inside
drain_commands() while holding effect_mutex_ (via effect_mutex_.try_lock()),
causing mixer updates to stall under contention; move handling of
AudioCommand::SetMixerGain out of the mutex-gated switch and into the lock-free
drain/command-processing path (the same place non-blocking commands are handled)
so calls to audio_shadow_executor_->update_mixer_gain(cmd.effect_index,
cmd.param_index, cmd.value) execute without acquiring effect_mutex_; update the
switch/dispatch in drain_commands() to skip SetMixerGain and add equivalent
handling in the lock-free/try-lock-failed path (or the drain_lock_free_*
routine) and ensure no other state requiring effect_mutex_ is touched when
processing this command.

In `@src/audio/engine/audio_graph_executor.cpp`:
- Around line 175-185: The update_mixer_gain implementation currently updates
any matching node and accepts raw gain; change
AudioGraphExecutor::update_mixer_gain to first verify the matched
execution_plan_ step is a mixer target (e.g., check a mixer marker such as
step.node_type == NodeType::Mixer or step.is_mixer) and only then iterate
step.input_sources, matching src.pin_index; when assigning src.gain, clamp the
incoming gain to the allowed range 0.0f–2.0f (e.g., std::clamp(gain, 0.0f,
2.0f)) and then break after handling the mixer step. Ensure you reference
step.node_id, step.input_sources, src.pin_index, and src.gain in the updated
logic.

In `@src/audio/engine/audio_graph.cpp`:
- Around line 361-374: In AudioGraph::remove_input_pin, protect against
input_gains/pop_back UB by verifying and reconciling sizes before calling
node.input_gains.pop_back(): check node.input_gains.size() is at least
node.input_pin_ids.size() (or non-empty) and if it is smaller, resize
node.input_gains to match node.input_pin_ids (filling with a sensible default
gain) or skip popping so sizes remain consistent; update the logic around
node.input_pin_ids.pop_back() / node.input_gains.pop_back() to ensure both
containers stay synchronized (references: AudioGraph::remove_input_pin,
node.input_pin_ids, node.input_gains, links_).

In `@src/gui/pedalboard/pedal_board_chain.cpp`:
- Around line 693-694: The draft wire control points use fixed offsets (ImVec2
cp1/ cp2) which don't respect zoom; modify the offsets from 45.0f to scale by
the current zoom (use ui_state.zoom) so cp1 and cp2 compute as p1.x + 45.0f *
ui_state.zoom and mouse_pos.x - 45.0f * ui_state.zoom (same pattern used by the
completed cable code around the completed-cable drawing logic) to keep draft
spline visuals consistent at different zoom levels.

In `@src/midi/midi_manager_mapping.cpp`:
- Around line 240-246: The code in midi_manager_mapping.cpp currently parses
mapping.param_name.substr(5) without verifying the parameter name prefix, which
can mis-assign mixer inputs; update the block that checks mapping.effect_name
("Mixer_") and node_id to first validate that mapping.param_name begins with the
expected "Gain " prefix (e.g., mapping.param_name.find("Gain ") == 0 or
starts_with("Gain ")), only then attempt to stoi the substring to produce
pin_idx; keep the existing try/catch around stoi and only proceed to the mixer
update when both node_id and the validated pin_idx are valid.

In `@tests/integration/test_audio_graph.cpp`:
- Around line 1012-1053: Add assertions to test gain clamping and lock-free
runtime updates: after setting initial gains with
graph.set_mixer_input_gain(mixer, ..) include calls that set out-of-range values
(e.g., -0.5f and 3.0f) and then rebuild/compile and check the effective summed
output via executor.process matches the clamped values (0.0 and 2.0
respectively). Then test dynamic updates by calling
executor.update_mixer_gain(mixer, index, newGain) after executor.compile(graph)
and before executor.process(...) and assert the processed output reflects the
new gain (e.g., 0.25 + 1.0 + 2.0). Use the existing input_audio/output_audio and
ASSERT_TRUE with std::abs(...) < 0.001f to validate results.
- Around line 987-1010: Extend the audio_graph_mixer_n_inputs test to cover the
pin-management constraints: after creating the Mixer node
(audio_graph_mixer_n_inputs / AudioGraph graph / node->input_pin_ids) add a
Source node via add_node and connect its output to the Mixer’s last input pin
using add_link (use graph.get_nodes() to find the Source output_pin_ids), then
assert that graph.remove_input_pin(mixer) returns false when that pin has an
active connection; next, assert the maximum limit by calling
graph.add_input_pin(mixer) up to 8 inputs and then assert the 9th add_input_pin
returns false; finally remove pins down to 2 and assert that removing below the
minimum (remove_input_pin) returns false, using node->input_pin_ids size checks
around each operation.

---

Outside diff comments:
In `@src/audio/engine/audio_graph.cpp`:
- Around line 17-23: The mixer/merge-sum creation uses num_inputs unchecked,
allowing values outside the 2–8 contract; change the calculation of
inputs_to_create in the NodeRoutingType::Mixer and NodeRoutingType::MergeSum
branch to clamp num_inputs into the range [2,8] (e.g. use std::clamp or manual
min/max) before allocating input_pin_ids, input_gains and assigning
output_pin_ids; ensure the rest of the block
(node.input_pin_ids.push_back(next_id_++), node.input_gains.assign(...),
node.output_pin_ids.push_back(...)) uses this clamped inputs_to_create value.

---

Nitpick comments:
In `@src/gui/pedalboard/pedal_board_chain.cpp`:
- Around line 343-347: The two empty conditionals around
gui_midi_->render_remove_mapping_item(effect_name, param_name) and
gui_midi_->render_learn_menu_item(effect_name, param_name) should be simplified:
remove the surrounding if(...) { } wrappers and call the methods directly (or if
the boolean return is intentionally significant, replace the empty bodies with a
brief comment like // return value intentionally ignored). Update the calls to
gui_midi_->render_remove_mapping_item(effect_name, param_name) and
gui_midi_->render_learn_menu_item(effect_name, param_name) accordingly to
eliminate the empty if blocks.
- Around line 546-547: Remove the debug printf that prints "MANUAL DROP HOVER
DETECTED!" — delete the printf call that references ui_state.active_src_pin_id
and pin_id in pedal_board_chain.cpp (the manual drop hover detection block) so
it no longer spams the console; if you want to keep conditional logging, replace
it with a proper debug/log macro (e.g., LOG_DEBUG) guarded by the project's
debug build flag instead of leaving the raw printf.
🪄 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: 0840b6a8-6050-4eb5-bf5c-f8094faae077

📥 Commits

Reviewing files that changed from the base of the PR and between 0bc8426 and 48b52f3.

📒 Files selected for processing (15)
  • 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.cpp
  • src/audio/engine/audio_graph.h
  • src/audio/engine/audio_graph_executor.cpp
  • src/audio/engine/audio_graph_executor.h
  • src/audio/utils/spsc_queue.h
  • src/gui/commands/command_graph.h
  • src/gui/pedalboard/pedal_board_chain.cpp
  • src/gui/pedalboard/pedal_board_menu.cpp
  • src/midi/midi_manager_mapping.cpp
  • tests/integration/test_audio_graph.cpp
  • tests/integration/test_clipboard_preset.cpp
  • tests/integration/test_json_serialization.cpp

Comment thread src/audio/engine/audio_engine_process.cpp Outdated
Comment thread src/audio/engine/audio_graph_executor.cpp
Comment thread src/audio/engine/audio_graph.cpp Outdated
Comment thread src/gui/pedalboard/pedal_board_chain.cpp Outdated
Comment thread src/midi/midi_manager_mapping.cpp
Comment thread tests/integration/test_audio_graph.cpp
Comment thread tests/integration/test_audio_graph.cpp

@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: 3

🤖 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/audio_graph.cpp`:
- Around line 395-409: The restore_input_pin function risks signed/unsigned and
out-of-bounds issues: change the index handling to compare a non-negative index
safely against node.input_pin_ids.size() (cast to size_t or use a local size_t
idx = static_cast<size_t>(index) after checking index >= 0), and before
inserting into node.input_gains ensure node.input_gains.size() >=
node.input_pin_ids.size() (or at least >= idx) by resizing or appending default
gain values so that inserting at input_gains.begin() + idx is safe; keep the
existing fallback push_back logic and the next_id_ update (next_id_ = pin_id +
1) unchanged.

In `@src/gui/commands/command_graph.h`:
- Around line 99-104: The loop that finds the matching input pin (iterating
n->input_pin_ids) reads n->input_gains[i] without ensuring i <
n->input_gains.size(), which can cause undefined behavior; update the code in
the block that sets idx and gain (the variables idx, gain and the loop over
n->input_pin_ids comparing to link.dest_pin_id) to check bounds before accessing
input_gains (e.g., only assign gain = n->input_gains[i] if i <
n->input_gains.size(), otherwise use a safe default like 1.0f or skip that entry
and continue), so the code never reads out-of-range from n->input_gains.
- Around line 236-241: The loop that finds the input pin index reads
node->input_gains[i] without bounds-checking, which can be out-of-range; update
the block that sets auto_removed_pin_index and auto_removed_pin_gain (the loop
iterating node->input_pin_ids and the variables
auto_removed_pin_index/auto_removed_pin_gain) to guard access into
node->input_gains by checking i < node->input_gains.size() before reading, and
if the gain is missing set a safe default (or leave auto_removed_pin_gain
unchanged) so you mirror the same safety fix used in RemoveGraphNodeCommand.
🪄 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: 779cbde6-0f2a-4076-9237-1dccc9bc0510

📥 Commits

Reviewing files that changed from the base of the PR and between 48b52f3 and a1b2700.

📒 Files selected for processing (5)
  • src/audio/engine/audio_graph.cpp
  • src/audio/engine/audio_graph.h
  • src/gui/commands/command_graph.h
  • src/gui/pedalboard/pedal_board_chain.cpp
  • src/gui/pedalboard/pedal_board_menu.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/audio/engine/audio_graph.h

Comment thread src/audio/engine/audio_graph.cpp
Comment thread src/gui/commands/command_graph.h
Comment thread src/gui/commands/command_graph.h

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/audio/engine/audio_engine_process.cpp (1)

246-263: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

SetMixerGain can be silently dropped when it is not at queue head.

If a non-gain command is at the front, drain_gain_commands() stops early. Later, drain_commands() pops SetMixerGain but does nothing (Line 301), so the gain update is lost.

Proposed fix (preserve updates in mutex path as fallback)
             case AudioCommand::SetMixerGain:
-                // Skip SetMixerGain in the mutex-gated path, it is handled lock-free
+                // Fallback: apply if this command reached mutex path
+                if (audio_shadow_executor_) {
+                    audio_shadow_executor_->update_mixer_gain(
+                        cmd.effect_index, cmd.param_index, cmd.value);
+                }
                 break;

Also applies to: 301-303

🤖 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/audio_engine_process.cpp` around lines 246 - 263,
drain_gain_commands currently breaks when a non-gain command sits at the queue
head, allowing a later pop in drain_commands to discard SetMixerGain updates;
fix by adding a fallback path in drain_commands to handle
AudioCommand::SetMixerGain when it is popped there: after popping the command in
drain_commands, detect cmd.type == AudioCommand::SetMixerGain and call
audio_shadow_executor_->update_mixer_gain(cmd.effect_index, cmd.param_index,
cmd.value) (guarded by the same mutex/nullable check used elsewhere for
audio_shadow_executor_), so mixer gain updates are preserved even if they
weren't processed in drain_gain_commands; keep existing handling for
SetInputGain/SetOutputGain in drain_gain_commands unchanged.
🤖 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.

Outside diff comments:
In `@src/audio/engine/audio_engine_process.cpp`:
- Around line 246-263: drain_gain_commands currently breaks when a non-gain
command sits at the queue head, allowing a later pop in drain_commands to
discard SetMixerGain updates; fix by adding a fallback path in drain_commands to
handle AudioCommand::SetMixerGain when it is popped there: after popping the
command in drain_commands, detect cmd.type == AudioCommand::SetMixerGain and
call audio_shadow_executor_->update_mixer_gain(cmd.effect_index,
cmd.param_index, cmd.value) (guarded by the same mutex/nullable check used
elsewhere for audio_shadow_executor_), so mixer gain updates are preserved even
if they weren't processed in drain_gain_commands; keep existing handling for
SetInputGain/SetOutputGain in drain_gain_commands unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c31dfe06-ec25-486c-bd93-26beffbcb66b

📥 Commits

Reviewing files that changed from the base of the PR and between a1b2700 and 5bfa18f.

📒 Files selected for processing (6)
  • src/audio/engine/audio_engine_process.cpp
  • src/audio/engine/audio_graph.cpp
  • src/audio/engine/audio_graph_executor.cpp
  • src/gui/pedalboard/pedal_board_chain.cpp
  • src/midi/midi_manager_mapping.cpp
  • tests/integration/test_audio_graph.cpp
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/integration/test_audio_graph.cpp
  • src/audio/engine/audio_graph_executor.cpp
  • src/audio/engine/audio_graph.cpp
  • src/gui/pedalboard/pedal_board_chain.cpp

@sudip-mondal-2002 sudip-mondal-2002 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

LGTM but need similar splitter as well

@sudip-mondal-2002 sudip-mondal-2002 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The changes are too big to properly go through, based on a cursory reading, LGTM

Comment on lines +346 to +369
bool AudioGraph::add_input_pin(int node_id) {
for (auto &node : nodes_) {
if (node.id == node_id && node.routing_type == NodeRoutingType::Mixer) {
if (node.input_pin_ids.size() < 8) {
node.input_pin_ids.push_back(next_id_++);
node.input_gains.push_back(1.0f);
// Do not necessarily need to rebuild topology if we just added an unconnected pin
return true;
}
return false;
}
}
return false;
}

bool AudioGraph::remove_input_pin(int node_id, int pin_id) {
for (auto &node : nodes_) {
if (node.id == node_id && (node.routing_type == NodeRoutingType::Mixer || node.routing_type == NodeRoutingType::MergeSum)) {
if (node.input_pin_ids.size() > 2) {
if (node.input_gains.size() < node.input_pin_ids.size()) {
node.input_gains.resize(node.input_pin_ids.size(), 1.0f);
}
int index_to_remove = -1;
if (pin_id == -1) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

There is a big refactor needed

the nodes should not be handled like this

there should be

class Inputnode: Node {
add_to_graph(Graph g){}
}

We need to restructure our codebase to proper OOP style

I dont like this functional programmings

Will take up that as refactoring task. No need to worry

@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: 1

🤖 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/audio_graph.cpp`:
- Around line 482-483: The signed/unsigned comparison in the insertion check
should be changed to use a size_t like the analogous restore_input_pin: convert
or replace the signed variable index with a size_t (e.g., size_t idx =
static_cast<size_t>(index)) and then use idx <= node.output_pin_ids.size() in
the condition before calling node.output_pin_ids.insert(... begin() + idx, ...).
Update the surrounding condition that currently reads "index >= 0 && index <=
node.output_pin_ids.size()" to use the new size_t variable to avoid
-Wsign-compare and match restore_input_pin's approach.
🪄 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: 025b234a-0edb-4038-a8cb-799913a282d2

📥 Commits

Reviewing files that changed from the base of the PR and between 5bfa18f and 7d9c1c5.

📒 Files selected for processing (6)
  • src/audio/engine/audio_graph.cpp
  • src/audio/engine/audio_graph.h
  • src/gui/commands/command_graph.h
  • src/gui/pedalboard/pedal_board_chain.cpp
  • src/gui/pedalboard/pedal_board_menu.cpp
  • tests/ui/test_pedal_board_menu.cpp
✅ Files skipped from review due to trivial changes (1)
  • tests/ui/test_pedal_board_menu.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/audio/engine/audio_graph.h
  • src/gui/pedalboard/pedal_board_menu.cpp
  • src/gui/pedalboard/pedal_board_chain.cpp

Comment on lines +482 to +483
if (index >= 0 && index <= node.output_pin_ids.size()) {
node.output_pin_ids.insert(node.output_pin_ids.begin() + index, pin_id);

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use a size_t comparison to match restore_input_pin.

index <= node.output_pin_ids.size() is a signed/unsigned comparison (-Wsign-compare). The analogous restore_input_pin was already converted to a size_t form; mirror it here for consistency.

🛡️ Proposed fix
-        if (index >= 0 && index <= node.output_pin_ids.size()) {
-            node.output_pin_ids.insert(node.output_pin_ids.begin() + index, pin_id);
+        if (index >= 0 && static_cast<size_t>(index) <= node.output_pin_ids.size()) {
+            node.output_pin_ids.insert(node.output_pin_ids.begin() + static_cast<size_t>(index), pin_id);
         } else {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (index >= 0 && index <= node.output_pin_ids.size()) {
node.output_pin_ids.insert(node.output_pin_ids.begin() + index, pin_id);
if (index >= 0 && static_cast<size_t>(index) <= node.output_pin_ids.size()) {
node.output_pin_ids.insert(node.output_pin_ids.begin() + static_cast<size_t>(index), pin_id);
🤖 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/audio_graph.cpp` around lines 482 - 483, The signed/unsigned
comparison in the insertion check should be changed to use a size_t like the
analogous restore_input_pin: convert or replace the signed variable index with a
size_t (e.g., size_t idx = static_cast<size_t>(index)) and then use idx <=
node.output_pin_ids.size() in the condition before calling
node.output_pin_ids.insert(... begin() + idx, ...). Update the surrounding
condition that currently reads "index >= 0 && index <=
node.output_pin_ids.size()" to use the new size_t variable to avoid
-Wsign-compare and match restore_input_pin's approach.

@MohitBareja16 MohitBareja16 added type:performance Performance optimization or latency improvement type:testing Unit tests, integration tests, or test coverage level:critical and removed level:advanced Advanced task · 45 GSSoC points labels May 30, 2026
@MohitBareja16 MohitBareja16 merged commit a4ecce3 into sudip-mondal-2002:main May 30, 2026
9 checks passed
github-actions Bot added a commit that referenced this pull request May 30, 2026
@MohitBareja16 MohitBareja16 added the gssoc:approved GSSoC 2026 contribution label Jun 1, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 3, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jun 13, 2026
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 DAG Related to DAG-based modular signal routing architecture gssoc:approved GSSoC 2026 contribution High Impact Significant user-facing improvement level:critical mentor:sudip-mondal-2002 quality:exceptional type:design type:feature New feature or request type:performance Performance optimization or latency improvement type:testing Unit tests, integration tests, or test coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: N-input Mixer node (beyond 2-in-1-out) for complex parallel rigs

2 participants