Skip to content

feat(audio/effects): AudioEffect trait + EffectChain + bypass/wet-dry infrastructure (#30)#104

Merged
thewrz merged 9 commits into
mainfrom
feat/issue-30
Jun 6, 2026
Merged

feat(audio/effects): AudioEffect trait + EffectChain + bypass/wet-dry infrastructure (#30)#104
thewrz merged 9 commits into
mainfrom
feat/issue-30

Conversation

@thewrz
Copy link
Copy Markdown
Collaborator

@thewrz thewrz commented Jun 4, 2026

Summary

  • Defines the AudioEffect trait and EffectChain composable pipeline (PR 1 of 6 for issue feat(audio): sound effects (reverb, pitch shift) #18 Voice Effects)
  • Adds EffectsError, EffectsCommand, EffectsEvent, and a Mixer stub as the integration seam
  • Adds fundsp = "0.23" to Cargo.toml per ADR-006 — no fundsp composition used yet, just establishing the dep

Closes #30

Files Changed

  • src/audio/effects/mod.rs (new) — AudioEffect trait with process(), set_param(), bypass(), set_bypass(), latency_samples()
  • src/audio/effects/chain.rs (new) — EffectChain: ordered pipeline, bypass, wet/dry, scratch-buffer ping-pong (no alloc in hot path)
  • src/audio/effects/commands.rs (new) — EffectsCommand / EffectsEvent enums
  • src/audio/mixer.rs (new) — Mixer stub holding EffectChain, process_block() passthrough
  • src/audio/error.rs — added EffectsError (ChainTooLong, ParamUnknown, IndexOutOfRange)
  • src/audio/engine.rs — added SetEffectBypass, SetEffectParam, SetEffectWetDry, SetEffectChainBypass to AudioCommand; EffectsLatencyChanged to AudioEvent; Mixer held in EngineCtx
  • src/app.rs — added EffectsLatencyChanged match arm (no-op, reserved for Phase 4B UI)
  • Cargo.tomlfundsp = "0.23"

Design decisions

  1. No software-level buffer processing in mixer.rs in this PR. The current architecture uses PipeWire graph links for mic routing (ADR-007) — there is no application-level mic capture buffer. mixer.rs is a stub integration seam: it holds an EffectChain and exposes process_block(input, rate) -> &[f32]. Future PRs will plug this into a pw_stream process callback when actual DSP is wired. When the chain is empty, process_block is a transparent passthrough — existing mic routing behavior is completely unchanged.

  2. fundsp added but not used. The crate is declared per ADR-006 to avoid a Cargo.toml change in every future effect PR. Actual fundsp graph composition (>> chains, | parallel, & mix) starts in PR 2+.

  3. Wet/dry mix at chain level, not per-effect. The issue specifies "wet/dry mix parameter on the chain." EffectChain.wet_dry is a f32 in [0.0, 1.0]: 0.0 = input passthrough, 1.0 = fully processed. Applied after all effects run.

  4. Bypass passthrough copies input → output directly. When chain_bypass == true OR all effects are bypassed OR the chain is empty, process() calls output.copy_from_slice(input) and returns immediately — no intermediate buffer touched.

  5. EffectsCommand / EffectsEvent as variants on existing enums. Added to AudioCommand / AudioEvent rather than separate channels, consistent with how all other commands (volume, passthrough, monitor device) work in this codebase. One channel, one match arm.

  6. EffectChain.process() uses pre-allocated scratch ping-pong — no alloc in the hot path. Strategy: copy input → scratch, then per effect: copy scratch → output, run effect.process(output, scratch). Final scratch → output copy. Scratch is sized at EffectChain::new(block_size) and grown only in push_effect() (cold path).

Test Plan

cargo test                     # 245 tests, 0 failures
cargo clippy -- -D warnings    # zero warnings
cargo build --release          # builds cleanly

New tests (28):

  • audio::effects::chain::tests::* — 15 tests covering passthrough, chained gains, bypass, wet/dry, error cases, latency
  • audio::effects::commands::tests::* — 4 tests (constructibility)
  • audio::effects::tests::* — 3 tests (trait via PassThrough mock)
  • audio::error::effects_error_tests::* — 3 tests
  • audio::mixer::tests::* — 3 tests

Summary by CodeRabbit

  • New Features
    • Added a user-facing audio effects system: ordered effect chains, mixer integration, per-effect and chain bypass, wet/dry mixing, per-effect parameter control, and latency reporting for UI.
  • Documentation
    • Added a detailed implementation plan and tests-first scaffolding for the effects subsystem.
  • Chore
    • Added an audio DSP dependency to support voice/effects processing.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Worried about impact? Review this PR in Change Stack to explore blast radius before you approve or request changes.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b784f018-3ccb-438d-ba99-3c61f7418db9

📥 Commits

Reviewing files that changed from the base of the PR and between 3541eca and 24a30cf.

📒 Files selected for processing (1)
  • src/audio/mixer.rs
💤 Files with no reviewable changes (1)
  • src/audio/mixer.rs

📝 Walkthrough

Walkthrough

Adds audio effects plumbing: fundsp dependency, EffectsError, AudioEffect trait, EffectsCommand/EffectsEvent enums, EffectChain pipeline, Mixer integration, engine wiring, module re-exports, and a detailed implementation plan.

Changes

Audio Effects Subsystem Plumbing

Layer / File(s) Summary
Dependency and error foundation
Cargo.toml, src/audio/error.rs
Adds fundsp = "0.23" and introduces EffectsError (ChainTooLong, ParamUnknown, IndexOutOfRange) with tests.
AudioEffect trait and module surface
src/audio/effects/mod.rs, docs
Declares effects submodules, re-exports effect types, and defines AudioEffect trait (process, set_param, bypass/set_bypass, latency_samples) with pass-through tests and task doc.
Control protocol: EffectsCommand and EffectsEvent
src/audio/effects/commands.rs
Introduces EffectsCommand (SetBypass, SetParam, SetWetDry, SetChainBypass) and EffectsEvent (LatencyChanged, ParamRejected) with constructibility tests.
Effect chain pipeline implementation
src/audio/effects/chain.rs, docs
Adds EffectChain with MAX_CHAIN_LEN = 16, bounded push/remove, per-effect and chain bypass, pre-allocated scratch buffer, ping-pong processing, wet/dry mixing, latency summation, and comprehensive unit tests; includes notes on avoiding per-call allocations.
Mixer integration seam
src/audio/mixer.rs, docs
Adds Mixer owning an EffectChain, output_buf, chain_mut(), process_block() and ensure_output_capacity() to run input through the chain and return processed output slice with dynamic resizing; includes tests.
Engine wiring and dispatcher
src/audio/engine.rs, src/app.rs, docs
Extends AudioCommand/AudioEvent with effect-control variants, adds mixer: Rc<RefCell<Mixer>> to EngineCtx, constructs Mixer::new(4096) in run_engine, routes effect commands to mixer chain methods, and adds AudioEvent::EffectsLatencyChanged handler stub in src/app.rs.
Module re-exports and audio mod updates
src/audio/mod.rs
Re-exports AudioEffect, EffectChain, EffectsCommand, EffectsEvent, and adds EffectsError to audio error re-exports.
Implementation plan documentation
docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md
Adds a detailed plan describing architecture, RT-safety and scratch-buffer strategy, bypass/wet-dry semantics, task checklist with TDD scaffolding, and spec coverage mapping.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Poem

A rabbit hums through buffers bright,
Chains of effects in dawning light,
Wet and dry in measured play,
Bypass hops quietly away,
Real-time-safe — the forest sings today. 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and specifically describes the main changes: introducing the AudioEffect trait, EffectChain, and bypass/wet-dry infrastructure, with a direct reference to the linked issue #30.
Linked Issues check ✅ Passed The PR fully implements all acceptance criteria from issue #30: AudioEffect trait with required methods, EffectChain with ordered processing, bypass/wet-dry infrastructure, EffectsError variants, comprehensive unit tests, Mixer integration point, and EffectsCommand/EffectsEvent enums for UI communication.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #30's plumbing objectives. The fundsp dependency declaration, trait definitions, chain implementation, error handling, mixer stub, command/event enums, and integration into AudioCommand/AudioEvent are all directly required or explicitly mentioned in the issue.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-30

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

cargo fmt --check failed on three files:
- audio/effects/commands.rs: collapse multi-line assert!(matches!(...)) to one line
- audio/engine.rs: expand SetEffectBypass struct variant to multi-line; collapse
  set_chain_bypass method chain to single line
- audio/mod.rs: reorder mod declarations alphabetically (effects before engine)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md (2)

820-875: ⚡ Quick win

Remove the unsafe implementation draft from the plan to prevent accidental adoption.

Even though you later provide a safe alternative, keeping an unsafe variant in the step-by-step plan increases the chance it gets copied into production code. Keeping only the final safe version would reduce ambiguity.

Also applies to: 877-927

🤖 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 `@docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md` around
lines 820 - 875, Remove the unsafe implementation draft that uses raw pointers
from the plan and keep only the final safe, non-allocating version of process();
specifically delete the code that takes scratch.as_ptr() and constructs a slice
via unsafe and the accompanying commentary, and ensure the retained text shows
the safe approach that uses self.scratch, output.copy_from_slice,
self.scratch[..n].copy_from_slice(&output[..n]),
effect.process(&self.scratch[..n], &mut output[..n], sample_rate) (or an
equivalent safe call) along with the wet/dry mix logic and references to
scratch, effects, effect.process, and wet_dry so readers cannot accidentally
copy the unsafe variant into production.

83-85: ⚡ Quick win

Use repo-relative commands instead of machine-specific absolute paths.

Hardcoding /home/adam/... makes the plan non-portable for other contributors and automation. Prefer repo-relative instructions (e.g., “run from repo root” and plain cargo ...).

Also applies to: 110-112, 154-156, 178-179, 265-267, 347-349, 812-814, 932-934, 987-989, 1096-1098, 1105-1107, 1114-1116, 1159-1161, 1221-1223, 1230-1232, 1239-1241

🤖 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 `@docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md` around
lines 83 - 85, The plan uses machine-specific absolute paths (e.g., the command
starting with "cd /home/adam/github/honkhonk/.worktrees/feat/issue-30" followed
by "cargo test effects_error 2>&1 | head -30") which makes it non-portable;
update those steps to be repo-relative by instructing contributors to run
commands from the repo root (or use a generic repo-root placeholder) and use
plain commands like "cargo test ..." and "head -30" without hardcoded /home/adam
paths; apply the same change to the other occurrences you listed (lines around
110-112, 154-156, 178-179, 265-267, 347-349, 812-814, 932-934, 987-989,
1096-1098, 1105-1107, 1114-1116, 1159-1161, 1221-1223, 1230-1232, 1239-1241) so
the document uses repo-relative instructions consistently.
🤖 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 `@docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md`:
- Line 7: Update the plan text to use the actual Mixer API method name
process_block() instead of process_if_active(): search the document for any
occurrence of process_if_active (including the three places called out) and
replace with process_block(), and ensure any surrounding wording describing the
seam or no-op stub refers to process_block() exposing EffectChain via the
engine; keep references to EffectChain, src/audio/mixer.rs, and EffectsError
unchanged so readers can map to the implemented symbols.

In `@src/audio/effects/mod.rs`:
- Around line 14-16: The set_param signature currently returns () so invalid or
unknown parameter cases cannot propagate typed errors; change fn set_param(&mut
self, param: &str, value: f32) to return Result<(), ParamError> (or another
thiserror-declared enum) and implement a ParamError using thiserror with
variants like UnknownParameter { name: String } and OutOfRange { name: String,
value: f32, range: (f32,f32) }; update all implementations of the Effect trait
(methods named set_param) to return Err(...) with contextual info when
validation/normalization fails, and update callers (command handler) to
propagate or .context() the error using anyhow as appropriate so structured
errors cross the boundary.

In `@src/audio/engine.rs`:
- Around line 410-423: The SetEffectBypass and SetEffectParam handlers currently
drop the Result from ctx.mixer.borrow_mut().chain_mut().set_bypass/set_param;
instead capture and surface errors with context (don’t unwrap or ignore). Update
the AudioCommand::SetEffectBypass and AudioCommand::SetEffectParam branches to
handle the Result: on Err call .context(...) (anyhow) at the call site with
clear info (e.g. include index and param name/value) and then either
return/propagate a typed error (use thiserror for module boundary types) or emit
a UI/error event so the UI is informed of invalid index/param; ensure the log or
propagated error message contains the origin ("set_bypass"/"set_param") and the
offending index/param values.

In `@src/audio/mixer.rs`:
- Around line 50-53: Mixer::process_block() performs heap allocation by growing
output_buf when n > output_capacity; move allocations out of the real-time path
by introducing a non-realtime method (e.g.,
Mixer::ensure_output_capacity(required_capacity)) that does the actual buffer
growth (call Vec::resize or reserve and initialize) and updates output_capacity,
and remove the resize call from Mixer::process_block(); in process_block()
replace the resize branch with a debug_assert or early clamp/path that assumes n
<= output_capacity and zeroes only the used portion of output_buf, ensuring no
heap allocation happens inside the audio callback.

---

Nitpick comments:
In `@docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md`:
- Around line 820-875: Remove the unsafe implementation draft that uses raw
pointers from the plan and keep only the final safe, non-allocating version of
process(); specifically delete the code that takes scratch.as_ptr() and
constructs a slice via unsafe and the accompanying commentary, and ensure the
retained text shows the safe approach that uses self.scratch,
output.copy_from_slice, self.scratch[..n].copy_from_slice(&output[..n]),
effect.process(&self.scratch[..n], &mut output[..n], sample_rate) (or an
equivalent safe call) along with the wet/dry mix logic and references to
scratch, effects, effect.process, and wet_dry so readers cannot accidentally
copy the unsafe variant into production.
- Around line 83-85: The plan uses machine-specific absolute paths (e.g., the
command starting with "cd /home/adam/github/honkhonk/.worktrees/feat/issue-30"
followed by "cargo test effects_error 2>&1 | head -30") which makes it
non-portable; update those steps to be repo-relative by instructing contributors
to run commands from the repo root (or use a generic repo-root placeholder) and
use plain commands like "cargo test ..." and "head -30" without hardcoded
/home/adam paths; apply the same change to the other occurrences you listed
(lines around 110-112, 154-156, 178-179, 265-267, 347-349, 812-814, 932-934,
987-989, 1096-1098, 1105-1107, 1114-1116, 1159-1161, 1221-1223, 1230-1232,
1239-1241) so the document uses repo-relative instructions consistently.
🪄 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 Plus

Run ID: d2c89b23-b1df-4879-aad5-8f9bba22b400

📥 Commits

Reviewing files that changed from the base of the PR and between 2c8c5cb and b41e1e9.

📒 Files selected for processing (10)
  • Cargo.toml
  • docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md
  • src/app.rs
  • src/audio/effects/chain.rs
  • src/audio/effects/commands.rs
  • src/audio/effects/mod.rs
  • src/audio/engine.rs
  • src/audio/error.rs
  • src/audio/mixer.rs
  • src/audio/mod.rs

Comment thread docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md Outdated
Comment thread src/audio/effects/chain.rs Outdated
Comment thread src/audio/effects/mod.rs Outdated
Comment thread src/audio/engine.rs
Comment thread src/audio/mixer.rs
…set_param

- chain.rs: replace allocating resize in process() hot path with passthrough
  when block size exceeds pre-allocated scratch_capacity; upholds RT contract
- mixer.rs: introduce ensure_output_capacity() cold-path pre-sizer; document
  that process_block() allocation is a fallback, not the intended RT path
- effects/mod.rs: make AudioEffect::set_param fallible (-> Result<(), EffectsError>)
  so unknown/invalid parameters carry structured context across module boundaries
- chain.rs: update GainEffect test impl to return Err(ParamUnknown) for unknown
  params; update EffectChain::set_param to propagate effect errors via ?
- engine.rs: surface set_bypass/set_param errors via AudioEvent::Error instead
  of silently discarding them; avoids silent state drift in the UI
- plan doc: align process_if_active() references to shipped process_block() name
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
src/audio/mixer.rs (1)

42-47: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove orphaned documentation comment.

Lines 42-47 contain the old doc comment for process_block, but that method now has updated documentation at lines 60-68. These lines should have been removed when ensure_output_capacity was inserted.

📝 Proposed fix
-    /// Process a block of mic audio through the effect chain.
-    ///
-    /// Returns a slice into the internal output buffer. Caller copies this
-    /// to the virtual sink's input buffer.
-    ///
-    /// No-op passthrough when chain is empty or bypassed. Real-time safe.
     /// Prepare `Mixer` to handle blocks up to `required_capacity` samples.
🤖 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/mixer.rs` around lines 42 - 47, Remove the orphaned documentation
block that used to document process_block (the old doc comment currently
preceding ensure_output_capacity); it duplicates the updated docs on
process_block and should be deleted so only the current doc comment remains
(keep the new documentation at the process_block definition and
ensure_output_capacity remains as-is).
♻️ Duplicate comments (1)
src/audio/mixer.rs (1)

71-75: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Heap allocation remains in the real-time hot path.

process_block() still performs heap allocation (via ensure_output_capacity(n)Vec::resize) when an incoming block exceeds the preallocated capacity. This violates the real-time safety constraint required for the PipeWire process callback and contradicts the past review guidance to replace the resize with a debug_assert or early clamp/passthrough.

Per the PR objectives: "Respect real-time constraints for the PipeWire thread (no allocs/locks/syscalls in hot path)" and commit messages reference "passthrough behavior when block size exceeds scratch capacity". Replace the allocation fallback with one of:

  • Option 1 (strictest): debug_assert!(n <= self.output_capacity); to catch capacity violations in debug builds while assuming correct pre-sizing in release.
  • Option 2 (graceful degradation): Clamp to self.output_capacity and process only the prefix, or return passthrough (copy input to output slice) when capacity is exceeded, avoiding allocation entirely.
🔧 Proposed fix (Option 1: debug_assert)
     pub fn process_block(&mut self, input: &[f32], sample_rate: u32) -> &[f32] {
         let n = input.len();
-        if n > self.output_capacity {
-            // Allocation fallback — avoid by calling ensure_output_capacity()
-            // on a cold path before the audio callback runs.
-            self.ensure_output_capacity(n);
-        }
+        debug_assert!(
+            n <= self.output_capacity,
+            "process_block called with block size {n} exceeding capacity {}; \
+             call ensure_output_capacity({n}) on a cold path before starting the audio callback",
+            self.output_capacity
+        );
+        let n = n.min(self.output_capacity);
         self.chain
             .process(input, &mut self.output_buf[..n], sample_rate);
         &self.output_buf[..n]
🤖 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/mixer.rs` around lines 71 - 75, The process_block hot path
currently calls ensure_output_capacity(n) causing a heap allocation when n >
self.output_capacity; replace that allocation fallback by removing the
ensure_output_capacity(n) call and instead enforce real-time safety: either
(preferred) add debug_assert!(n <= self.output_capacity) in process_block to
catch violations in debug builds and assume pre-sizing in release, or implement
graceful degradation by clamping n to self.output_capacity (process only the
prefix) or performing a passthrough copy from input to output without growing
buffers; update references to ensure_output_capacity, process_block, and
output_capacity accordingly and remove any Vec::resize fallback so no
allocations occur in the PipeWire callback.
🤖 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/mixer.rs`:
- Around line 42-47: Remove the orphaned documentation block that used to
document process_block (the old doc comment currently preceding
ensure_output_capacity); it duplicates the updated docs on process_block and
should be deleted so only the current doc comment remains (keep the new
documentation at the process_block definition and ensure_output_capacity remains
as-is).

---

Duplicate comments:
In `@src/audio/mixer.rs`:
- Around line 71-75: The process_block hot path currently calls
ensure_output_capacity(n) causing a heap allocation when n >
self.output_capacity; replace that allocation fallback by removing the
ensure_output_capacity(n) call and instead enforce real-time safety: either
(preferred) add debug_assert!(n <= self.output_capacity) in process_block to
catch violations in debug builds and assume pre-sizing in release, or implement
graceful degradation by clamping n to self.output_capacity (process only the
prefix) or performing a passthrough copy from input to output without growing
buffers; update references to ensure_output_capacity, process_block, and
output_capacity accordingly and remove any Vec::resize fallback so no
allocations occur in the PipeWire callback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 62e905b3-878d-41b2-aa8c-46b9b564f233

📥 Commits

Reviewing files that changed from the base of the PR and between a9d4de0 and 3541eca.

📒 Files selected for processing (5)
  • docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md
  • src/audio/effects/chain.rs
  • src/audio/effects/mod.rs
  • src/audio/engine.rs
  • src/audio/mixer.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/audio/effects/mod.rs
  • src/audio/engine.rs
  • src/audio/effects/chain.rs
  • docs/superpowers/plans/2026-06-04-issue-30-effect-chain-plumbing.md

Lines 42-47 in mixer.rs retained the old process_block doc block after
ensure_output_capacity was inserted between chain_mut and process_block.
The stale comment described the wrong method. Deleted the orphaned lines;
ensure_output_capacity now has only its own doc, and process_block's doc
at lines 54-62 is unambiguous.

Flagged by CodeRabbit (outside-diff comment, first review round).
@thewrz
Copy link
Copy Markdown
Collaborator Author

thewrz commented Jun 6, 2026

Fixed the orphaned doc comment in src/audio/mixer.rs flagged in the first CodeRabbit review (outside-diff comment at lines 42-47). The stale process_block doc block that preceded ensure_output_capacity has been removed; each method now has only its own doc comment. Fixed in commit 24a30cf.

@thewrz thewrz merged commit 4c6e59b into main Jun 6, 2026
9 checks passed
@thewrz thewrz deleted the feat/issue-30 branch June 6, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(audio): effect chain trait + bypass pipeline

1 participant