Skip to content

feat: add Gate audio effect (noise gate) completing the dynamics set#2072

Open
yuto-trd wants to merge 5 commits into
mainfrom
loop/audio-dynamics
Open

feat: add Gate audio effect (noise gate) completing the dynamics set#2072
yuto-trd wants to merge 5 commits into
mainfrom
loop/audio-dynamics

Conversation

@yuto-trd

@yuto-trd yuto-trd commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

Adds a Gate (noise gate) audio effect, completing the dynamics-processing set alongside the already-merged Compressor (#1724) and Limiter (#1725):

  • GateEffect (new, Beutl.Engine.Audio.Effects) — animatable Threshold / Attack / Hold / Release / Range properties, faithful clone of the CompressorEffect surface pattern (ScanProperties, [Range]/[Display] attributes, CreateNode via AddNode + Connect).
  • GateNode (new, Beutl.Engine.Audio.Graph.Nodes) — per-sample DSP mirroring CompressorNode: static/animated paths sharing one gate-gain helper, linked-stereo peak detection, hold-timer latching, one-pole dB-domain attack/release envelope, pooled per-channel handle cache, envelope continuity across chunk boundaries, reset on sample-rate/time discontinuity, non-finite parameter sanitization.
  • GateParameters (internal) — shared ranges/defaults, same convention as CompressorParameters/LimiterParameters.
  • LibraryRegistrar registration + EN/JA display strings, so Gate appears in the audio-effect add menu (parameter UI is auto-generated by AudioEffectEditor).

All additive — no existing type's signature changes; non-breaking feat:.

Board-item scope note

The board item's 現状把握 predates #1724/#1725: Compressor and Limiter are already on main with full tests, so only Gate remained in the dynamics family. NoiseSuppression (SpectralGate) is a distinct FFT/STFT architecture that the item body itself splits into a separate phase; it is intentionally not part of this PR (no TODO markers left in code — the phase boundary is documented here and in the commit body). Future naming is collision-free (SpectralGate* vs Gate*).

Tests

  • GateNodeTests + GateEffectTests — 34 new tests: silence input, threshold open/close behavior, attack/release time characteristics, hold latching, range floor, chunk-split processing equals single-block processing (glitch guard), parameter sanitization, node reuse/reset.
  • Full Engine.Audio namespace: 234 passed / 0 failed (no regression).
  • Local gates: builds 0 errors; dotnet format --verify-no-changes clean; UTF-8 BOM on all new .cs files.

Review

  • Pre-PR design review: approve (pass 1, zero rework findings) — pattern-consistent with Compressor/Limiter, orthogonal property vocabulary, additive surface. Noted (out of scope): the three dynamics nodes share duplicated scaffolding that could someday be a joint DynamicsNode refactor across all three.

Manual verification

  1. Real-time playback: gate an intermittent-speech clip, confirm no clicks at open/close transitions across buffer boundaries.
  2. Export parity with preview (same GateNode drives both).
  3. Undo/redo of parameter edits (CoreProperty-based).
  4. "Gate" appears in the audio-effect add menu with correct EN/JA labels.

Refs: Project #9 board item「オーディオエフェクト拡充 (Compressor / Limiter / NoiseSuppression)」

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a new audio Gate effect with animatable Threshold, Attack, Hold, Release, and Range controls.
    • Added localized UI labels and descriptions for gate parameters (including Japanese) and included the effect in the audio effect library.
  • Bug Fixes
    • Improved gate stability and continuity across renders, chunk boundaries, and sample-rate changes.
    • Enhanced handling for digital silence, non-finite values, and exact Range = 0 passthrough.
  • Tests
    • Added unit tests covering effect/node wiring, default values, and both static and animated processing paths.

Add GateEffect/GateNode, a downward noise gate completing the dynamics-
processing set alongside the already-shipped Compressor and Limiter. The
gate attenuates the signal by a configurable Range while its level sits
below Threshold, with Attack/Hold/Release envelope timing:

- GateEffect: additive public AudioEffect exposing Threshold (dB), Attack
  (ms), Hold (ms), Release (ms) and Range (dB) as animatable CoreProperty-
  backed IProperty<float>, mirroring CompressorEffect's declaration style.
- GateNode: per-sample DSP following the CompressorNode pattern exactly —
  static/animated Process paths sharing one gate-gain helper, linked-stereo
  peak detection, hold-timer latching, one-pole attack/release smoothing in
  the dB domain, cached per-channel buffer handles on the hot loop, envelope
  continuity across chunks, reset on sample-rate change / time discontinuity,
  and once-per-parameter non-finite/out-of-range sanitization with recovery.
- GateParameters: single source of truth for ranges/defaults shared by the
  effect's [Range] attributes and the node's clamps.
- Registered in LibraryRegistrar so it appears in the audio-effect add menu;
  EN/JA display names and descriptions added to AudioStrings.

Tests: GateNodeTests (DSP behaviour — silence, open/close about threshold,
attack time constant, hold latching, range=0 bypass, chunk/seek/sample-rate
state continuity, Reset, animated path, static/animated parity, non-finite
recovery, mono, zero-length, arity guards) and GateEffectTests (property
wiring, defaults, [Range] validators, range consistency). 34 new tests green;
full Engine.Audio suite (234) green.

NoiseSuppression (SpectralGate) is intentionally NOT included here: the board
item's own body phases it separately (phase 1 C# SpectralGate, phase 2
RNNoise/ONNX out of scope), and it is a distinct FFT-based architecture rather
than an envelope-follower dynamics processor. It is tracked as a follow-on
phase of the same board item, not deferred in-scope work.

Refs: Project #9 board item "オーディオエフェクト拡充 (Compressor / Limiter / NoiseSuppression)"
Copilot AI review requested due to automatic review settings July 7, 2026 21:22
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

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

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: 2e791a1b-d635-4a59-849d-41b33836bc98

📥 Commits

Reviewing files that changed from the base of the PR and between 5be4d8a and 7ce866d.

📒 Files selected for processing (2)
  • src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
  • tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs

📝 Walkthrough

Walkthrough

This PR adds a noise gate audio effect with shared parameter definitions, validated animatable properties, static and animated DSP processing, state management, localization, catalog registration, and comprehensive unit tests.

Changes

Gate Effect Feature

Layer / File(s) Summary
Gate parameters and effect wiring
src/Beutl.Engine/Audio/Effects/GateParameters.cs, src/Beutl.Engine/Audio/Effects/GateEffect.cs, tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs
Defines gate parameter ranges and defaults, exposes validated animatable properties, creates and connects GateNode, and tests property wiring and validation.
GateNode processing entrypoint
src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Validates inputs, handles continuity resets and empty buffers, and selects static or animated processing.
Static and animated DSP paths
src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Processes static parameters directly and animated parameters in chunks with channel mapping, parameter sanitization, peak detection, and gain updates.
Gain logic and lifecycle state
src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Implements smoothing, hold behavior, parameter/output safeguards, reset handling, diagnostics, disposal, and effective parameter storage.
Catalog registration and localization
src/Beutl.Language/AudioStrings.resx, src/Beutl.Language/AudioStrings.ja.resx, src/Beutl/Services/LibraryRegistrar.cs
Adds English and Japanese Gate resources and registers the effect in the audio effects catalog.
GateNode behavior validation
tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs
Tests gating behavior, animation, timing, continuity, resets, channel handling, non-finite recovery, and edge cases.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AudioContext
  participant GateEffect
  participant GateNode
  participant AudioBuffer

  AudioContext->>GateEffect: CreateNode(context, inputNode)
  GateEffect->>GateNode: create with gate parameter properties
  GateEffect->>AudioContext: connect input node to GateNode
  GateEffect-->>AudioContext: return GateNode
  AudioContext->>GateNode: Process(context)
  alt parameters animated
    GateNode->>GateNode: sample chunks and process animated values
  else parameters static
    GateNode->>GateNode: process static values
  end
  GateNode->>AudioBuffer: write sanitized gated samples
  GateNode-->>AudioContext: return output buffer
Loading
🚥 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 title clearly summarizes the main change: adding a Gate noise-gate audio effect to complete the dynamics set.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 loop/audio-dynamics

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.

@drift-check

drift-check Bot commented Jul 7, 2026

Copy link
Copy Markdown

Code Review Bot

No comment/code divergences or documentation drift detected. Reviewed 8 file(s); skipped 0.

Copilot AI 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.

Pull request overview

Adds a new dynamics audio effect Gate (noise gate) to Beutl’s audio engine and registers it in the app library, including localization and NUnit coverage. This completes the dynamics set alongside the already-added Compressor and Limiter.

Changes:

  • Added GateEffect + shared GateParameters (animatable properties, range metadata, node creation/wiring).
  • Added GateNode DSP implementation (linked peak detection, hold + attack/release smoothing, non-finite + clamp hardening, chunk-continuity behavior).
  • Added comprehensive unit tests and EN/JA localization strings; registered the effect in LibraryRegistrar.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs DSP/state/robustness coverage for GateNode including chunk continuity, resets, and non-finite handling.
tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs Verifies effect→node wiring, defaults vs GateParameters, and ScanProperties/range validators.
src/Beutl/Services/LibraryRegistrar.cs Registers GateEffect so it appears in the audio-effect library menu.
src/Beutl.Language/AudioStrings.resx Adds English UI strings for Gate + parameters/descriptions.
src/Beutl.Language/AudioStrings.ja.resx Adds Japanese UI strings for Gate + parameters/descriptions.
src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Implements the gate DSP node with static/animated paths and parameter/output sanitization.
src/Beutl.Engine/Audio/Effects/GateParameters.cs Centralizes Gate ranges/defaults shared by effect metadata and DSP clamps.
src/Beutl.Engine/Audio/Effects/GateEffect.cs Defines the public audio effect surface (properties + node creation) with localization metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs (2)

18-47: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication between PeakDb and PeakDbInWindow.

PeakDb could be expressed as PeakDbInWindow(buffer, startSample, buffer.SampleCount - startSample) to avoid maintaining two near-identical peak-scan loops.

🤖 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 `@tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs` around lines 18 - 47,
There is duplicated peak-scan logic between PeakDb and PeakDbInWindow in
GateNodeTests. Refactor PeakDb to delegate to PeakDbInWindow by passing the
remaining buffer length, and keep PeakDbInWindow as the single loop
implementation for computing the peak over a range.

611-647: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

White-box coupling to internal chunk size is acceptable but fragile.

This test hardcodes chunkSize = 1024, matching ProcessAnimated's internal chunking. If that internal constant changes, the test will silently start failing rather than pointing at the cause. Given it's an intentional continuity check per the comment, no action required, just flagging for future maintainers.

🤖 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 `@tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs` around lines 611 - 647,
The Process_AnimatedPath_SmoothAcrossChunkBoundary test is tightly coupled to
ProcessAnimated’s internal chunk size via the hardcoded chunkSize value. Replace
the literal with the shared chunk-size source used by GateNode.ProcessAnimated,
or route the assertion through a helper/constant so the test stays aligned if
chunking changes. Keep the continuity check logic and the references to
KeyFrameAnimation, GateNode, and ProcessAnimated intact.
tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs (1)

70-112: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider unifying duplicated (min, default, max) tuples.

AssertNameAndRange (Lines 77-81) and ParameterRanges() (Lines 98-105) both enumerate the same five parameter triples. A shared TestCaseSource would avoid keeping two lists in sync as parameters evolve.

🤖 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 `@tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs` around lines 70 - 112,
The tests duplicate the same min/default/max tuples in AssertNameAndRange and
ParameterRanges, so keep the GateEffect parameter set in one shared source
instead of two separate lists. Refactor the GateEffectTests helpers so both the
property validation in
ScanProperties_RegistersNamesAndRangeValidatorsForEveryProperty and the range
assertions in GateParameters_RangeIsConsistent consume a single TestCaseSource
or shared tuple provider, using the existing symbols like ParameterRanges and
AssertNameAndRange to locate the change.
🤖 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.

Nitpick comments:
In `@tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs`:
- Around line 70-112: The tests duplicate the same min/default/max tuples in
AssertNameAndRange and ParameterRanges, so keep the GateEffect parameter set in
one shared source instead of two separate lists. Refactor the GateEffectTests
helpers so both the property validation in
ScanProperties_RegistersNamesAndRangeValidatorsForEveryProperty and the range
assertions in GateParameters_RangeIsConsistent consume a single TestCaseSource
or shared tuple provider, using the existing symbols like ParameterRanges and
AssertNameAndRange to locate the change.

In `@tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs`:
- Around line 18-47: There is duplicated peak-scan logic between PeakDb and
PeakDbInWindow in GateNodeTests. Refactor PeakDb to delegate to PeakDbInWindow
by passing the remaining buffer length, and keep PeakDbInWindow as the single
loop implementation for computing the peak over a range.
- Around line 611-647: The Process_AnimatedPath_SmoothAcrossChunkBoundary test
is tightly coupled to ProcessAnimated’s internal chunk size via the hardcoded
chunkSize value. Replace the literal with the shared chunk-size source used by
GateNode.ProcessAnimated, or route the assertion through a helper/constant so
the test stays aligned if chunking changes. Keep the continuity check logic and
the references to KeyFrameAnimation, GateNode, and ProcessAnimated intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fb3b5d70-ca00-42fc-98e4-68e07b242e25

📥 Commits

Reviewing files that changed from the base of the PR and between e2dd0ab and 1b3650c.

📒 Files selected for processing (8)
  • src/Beutl.Engine/Audio/Effects/GateEffect.cs
  • src/Beutl.Engine/Audio/Effects/GateParameters.cs
  • src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
  • src/Beutl.Language/AudioStrings.ja.resx
  • src/Beutl.Language/AudioStrings.resx
  • src/Beutl/Services/LibraryRegistrar.cs
  • tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs
  • tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b3650cda7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Outdated
Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Outdated
Address three DSP correctness findings on the noise gate:

1. Range=0 is now an exact identity. Previously the gate-gain follower still
   smoothed toward 0 dB from its closed floor, so "gating disabled" (Range=0)
   introduced a startup attack ramp and an asymptotic sub-unity gain. NextGain
   now snaps the follower open and returns unit gain when Range >= 0, so output
   equals input sample-for-sample.

2. Linked-stereo peak detection no longer propagates NaN. The mono/stereo fast
   path seeded `peak = Abs(s0)`, so a NaN in one channel poisoned the peak
   (`a1 > NaN` is false) and mis-gated a valid channel. It now seeds 0 and
   compares (NaN > 0 is false), matching the >2-channel path, so a finite
   channel still drives the gate.

3. Digital silence no longer opens the gate at the -100 dB minimum Threshold.
   Silence collapses to inputDb = MinDb (-100), which compared equal to the
   minimum threshold (-100 >= -100) and opened the gate. Opening now also
   requires actual signal energy (peak > 0f), so -inf dB silence stays closed.

Each fix has a regression test (Process_RangeZero_IsExactIdentity,
Process_OneChannelNaN_GatesFromValidChannel,
Process_MinThreshold_DigitalSilenceDoesNotOpenGate); all three fail against the
prior GateNode and pass after the fix. Full Engine.Audio suite (237) green,
dotnet format clean.

Note: finding 2 (fast-path NaN peak seeding) also exists in the already-merged
CompressorNode (its >2-channel path is safe; the mono/stereo fast path is not);
LimiterNode is unaffected (it coerces non-finite samples to 0 first). That
existing-node fix is intentionally out of scope for this PR.

Refs: Project #9 board item "オーディオエフェクト拡充 (Compressor / Limiter / NoiseSuppression)"

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de710f38b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Outdated
…ding

Address two Codex review findings on GateNode:

- Seed the gain follower at the effective Range floor on the first sample
  after a reset instead of the -100 dB sentinel, so a below-threshold
  lead-in rests at the user's Range floor rather than fading in from
  near-silence. An above-threshold onset still ramps open from that floor.
- Match LimiterNode's one-tick timestamp tolerance when detecting chunk
  discontinuity, so adjacent sample-boundary buffers whose start differs by
  one tick from independent TimeSpan rounding are not misread as a seek and
  do not spuriously reset the gate.

Regression tests (red on the previous commit):
Process_BelowThresholdLeadIn_StartsAtRangeFloorNotFullMute,
Process_OneTickBoundaryRounding_DoesNotResetGate.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs`:
- Around line 755-766: The test Process_OneTickBoundaryRounding_DoesNotResetGate
currently covers only a one-tick overlap; add coverage for a one-tick gap by
also creating the second context with chunkDuration + TimeSpan.FromTicks(1), and
verify the warmed-open gate remains continuous rather than resetting.
🪄 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: c360737f-785e-4657-ab08-8b19a289cc8e

📥 Commits

Reviewing files that changed from the base of the PR and between de710f3 and f4bb293.

📒 Files selected for processing (2)
  • src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
  • tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs

Comment thread tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs Outdated
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Purely additive change with no modifications to existing types; all new code is well-guarded and pattern-consistent with sibling nodes.

The DSP logic is carefully implemented: the envelope follower, hold latch, Range-floor priming, and non-finite recovery all behave correctly on inspection, and the 34 tests exercise the edge cases (silence, attack/release time constants, hold off-by-one, chunk-boundary continuity, seek/sample-rate resets, NaN/Infinity inputs). No existing API surface is changed.

Files Needing Attention: The channels > 2 branches in ProcessStatic and ProcessAnimated have no test coverage, as noted in the prior review. Everything else is well-exercised.

Important Files Changed

Filename Overview
src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Core DSP implementation: one-pole IIR envelope follower with hold latch, linked-stereo peak detection, seeded priming at Range floor, timestamp-contiguity tolerance, and finite-output guardrails. Logic is sound and mirrors CompressorNode/LimiterNode conventions closely.
src/Beutl.Engine/Audio/Effects/GateEffect.cs AudioEffect surface layer: five animatable properties correctly forwarded by reference to GateNode in CreateNode. Pattern is identical to CompressorEffect.
src/Beutl.Engine/Audio/Effects/GateParameters.cs Constants file providing ranges and defaults shared between GateEffect's [Range] attributes and GateNode's clamping — prevents drift between the two layers.
tests/Beutl.UnitTests/Engine/Audio/GateNodeTests.cs 34 tests covering silence, threshold open/close, attack time constant, hold latching, chunk-split continuity, seek reset, sample-rate reset, Range=0 identity, non-finite input/parameter recovery, and boundary-rounding tolerance. Mono and stereo paths covered; channels > 2 path has no coverage (noted in prior review).
tests/Beutl.UnitTests/Engine/Audio/GateEffectTests.cs Effect-layer tests: property-reference wiring, input connection, default values, and [Range] validator registration. Good coverage of the effect surface.
src/Beutl/Services/LibraryRegistrar.cs One-line addition registering GateEffect in the audio-effect add menu. Correct placement after Limiter.
src/Beutl.Language/AudioStrings.resx English resource strings for all five Gate parameters and descriptions. All keys match the nameof() references in GateEffect.
src/Beutl.Language/AudioStrings.ja.resx Japanese localization: accurate translations of all Gate strings, consistent with existing Compressor/Limiter entries.

Reviews (3): Last reviewed commit: "fix(engine): correct Gate hold length, r..." | Re-trigger Greptile

Address a CodeRabbit review: Process_OneTickBoundaryRounding_DoesNotResetGate
only exercised a one-tick overlap. The discontinuity tolerance is symmetric
(±1 tick), so parametrize the test over both a one-tick overlap (-1) and a
one-tick gap (+1); an asymmetric implementation could pass one direction and
reset on the other.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5be4d8ad62

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Outdated
Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs
Comment thread src/Beutl.Engine/Audio/Graph/Nodes/GateNode.cs Outdated
Address three Codex review findings on GateNode:

- Read the hold latch for the current sample before spending it. Decrementing
  first kept the gate open for only N-1 of N configured hold samples, and a
  hold that converts to a single sample released immediately.
- Reject an upstream buffer whose sample rate differs from the process
  context, as LimiterNode already does. Coefficients and the hold count come
  from the context rate while the output buffer carries the input rate, so a
  mismatch mislabelled the samples instead of resampling them.
- Fall back to the declared GateParameters constant when a property's
  DefaultValue is itself non-finite. Returning a non-finite default defeated
  the range clamp (NaN clamps to NaN) and pinned the gate to its recovery
  sentinel.

Regression tests (all red on the previous commit):
Process_Hold_KeepsGateOpenForEveryConfiguredSample,
Process_InputSampleRateMismatch_Throws,
Process_NonFinitePropertyDefault_FallsBackToDeclaredConstant.
@github-actions

Copy link
Copy Markdown
Contributor

No TODO comments were found.

@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage

Package Line Rate Branch Rate Complexity Health
Beutl 19% 12% 10836
Beutl.AgentToolkit 90% 75% 7414
Beutl.Api 24% 15% 1410
Beutl.Configuration 63% 39% 419
Beutl.Controls 30% 14% 5517
Beutl.Core 69% 60% 3105
Beutl.Editor 82% 79% 2857
Beutl.Editor.Components 20% 13% 9780
Beutl.Embedding.MediaFoundation 6% 8% 1374
Beutl.Engine 69% 58% 19278
Beutl.Engine.SourceGenerators 59% 44% 540
Beutl.ExceptionHandler 0% 0% 45
Beutl.Extensibility 66% 75% 161
Beutl.Extensions.AVFoundation 0% 2% 202
Beutl.Extensions.FFmpeg 28% 26% 738
Beutl.Extensions.FFmpeg.Core 49% 30% 323
Beutl.FFmpegIpc 27% 35% 858
Beutl.FFmpegWorker 4% 4% 898
Beutl.Language 46% 50% 1507
Beutl.NodeGraph 26% 17% 2477
Beutl.PackageTools.UI 0% 0% 670
Beutl.ProjectSystem 75% 61% 1266
Beutl.Testing.Headless 88% 92% 15
Beutl.Threading 100% 90% 137
Beutl.Utilities 94% 87% 358
Beutl.WaitingDialog 0% 0% 36
Iciclecreek.Avalonia.Terminal 32% 21% 1164
XTerm.NET 15% 7% 2009
Summary 45% (89435 / 197156) 37% (21464 / 58437) 75394

Minimum allowed line rate is 0%

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.

2 participants