Skip to content

fix(editor): keep the preview's boosted audio from clipping at the speakers - #918

Merged
EtienneLescot merged 1 commit into
mainfrom
claude/preview-audio-limiter
Sep 30, 2026
Merged

EtienneLescot merged 1 commit into
mainfrom
claude/preview-audio-limiter

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #911. Second defect found while isolating the crackle; the capture holes themselves are fixed in #916.

Cause

  • The editor preview raises a take by its loudness gain (up to +12 dB) and the output trim, with no limiter.
  • A loud take's peaks go past full scale, and the audio device clips them.
  • The export does not: its limiter holds them at −1.5 dBFS.
  • Measured on a real take: a −7.1 dBFS peak raised 8.9 dB, 92 samples over full scale.

Fix

  • The mix reaches the speakers through a WaveShaperNode: the identity up to the export's ceiling, then a tanh knee towards full scale.
  • A curve has no state, which keeps the file's rule that the preview must not drift from the export: everything under the ceiling plays exactly as exported.
  • A headroom gain (÷16, +24 dB) brings the mix into the shaper's [−1, 1] input range.

Verification

Rendered in Chromium with an OfflineAudioContext (440 Hz sine, 1 s):

Input Output peak Samples over full scale
0.25 / 0.5 / 0.84 unchanged (error −120 dB) 0
1.226 (+1.77 dBFS, the measured case) −0.02 dBFS 0 (18800 without the stage)
×20 (+26 dB) 0 dBFS 0

Tests:

  • The curve under the Web Audio spec's WaveShaper semantics: identity under the ceiling, never past full scale, no corner at the knee.
  • The graph wiring: the speakers are reached only through the ceiling. A mutant that bypasses it fails.
  • src/components/ai-edition passes, except LeftPanel.copyMessage, which fails under load and passes alone (unrelated).
  • tsc (app and tests) and Biome pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Audio previews now apply a smooth output ceiling, preserving levels up to the export ceiling and gently limiting peaks above it. This keeps boosted preview audio bounded while providing a consistent transition at the ceiling, bringing preview playback closer to the final exported audio.

…eakers

The preview raises a take by its loudness gain (up to +12 dB) and the
output trim, with no limiter, so a loud take's peaks went past full scale
and the audio device clipped them. The export holds them at -1.5 dBFS.
Measured on a real take: a -7.1 dBFS peak raised 8.9 dB, 92 samples over.

The mix now reaches the speakers through a WaveShaperNode: the identity
up to the export's ceiling, then a tanh knee towards full scale. A curve
has no state, so the preview still plays everything under the ceiling
exactly as the export writes it. Checked in Chromium with an
OfflineAudioContext: -120 dB error under the ceiling, and a +1.77 dBFS
sine goes from 18800 samples over full scale to none.

Refs #911
@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 61153bb7-1ed4-4f55-8b9a-d9891c5834db

📥 Commits

Reviewing files that changed from the base of the PR and between bc6fa48 and ef533c1.

📒 Files selected for processing (3)
  • src/components/ai-edition/VirtualPreview.audio.test.ts
  • src/components/ai-edition/VirtualPreview.playback.test.tsx
  • src/components/ai-edition/VirtualPreview.tsx

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The preview audio graph now applies inverse headroom gain and a WaveShaper ceiling curve before audio reaches the destination. The curve preserves levels through the export ceiling and applies a tanh knee above it. Cleanup and tests cover the added nodes and routing.

Changes

Preview audio ceiling

Layer / File(s) Summary
Ceiling curve and contract
src/components/ai-edition/VirtualPreview.tsx, src/components/ai-edition/VirtualPreview.audio.test.ts
The preview graph shape adds headroom and ceiling nodes. The curve preserves levels through the export ceiling and applies a tanh knee above it. Tests cover bounded monotonic output, odd symmetry, and slope continuity.
Audio routing and cleanup
src/components/ai-edition/VirtualPreview.tsx, src/components/ai-edition/VirtualPreview.playback.test.tsx
Graph construction routes output through the headroom gain and ceiling WaveShaper to the destination. Cleanup disconnects the added nodes. Playback tests check the WaveShaper curve and output connections.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix

Suggested reviewers: beetix

Merge Risk: ⚪ Minimal · up to ef533

The preview ceiling preserves ordinary playback levels and bounds boosted peaks, with correct routing and cleanup. No actionable merge-blocking issue remains; merge after normal checks.

Security Architecture Review

Security architecture risk: ⚪ Minimal · up to ef533

The change remains within browser-local audio playback and introduces no observed new privileges, data exposure, or network access. Successful playback gains an amplitude ceiling. Existing fallback paths do not provide the same ceiling guarantee.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The supported exposure is the component's browser-local audio context and audible output. Existing media and gain inputs reach the same destination; the changed routing does not establish expanded tenant, service, or data-store access.

Trust Boundaries and Controls

  • observed — The WaveShaper is an amplitude control on successfully routed playback, not a universal media-security boundary. Existing local fallback paths can omit it; comparison with base did not show that this change creates or amplifies those bypasses.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: preventing boosted preview audio from clipping at the speakers.
Description check ✅ Passed The description clearly explains the defect, cause, fix, issue reference, implementation, and verification results. It does not use every template heading and does not identify the change type, releas…
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.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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.

@EtienneLescot
EtienneLescot merged commit 9446101 into main Sep 30, 2026
19 checks passed
@EtienneLescot
EtienneLescot deleted the claude/preview-audio-limiter branch September 30, 2026 22: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.

1 participant