Skip to content

feat(audio-mixer): honor ramp_ms on mute toggles, with cancel-guard - #540

Merged
srperens merged 1 commit into
mainfrom
feat/audio-mixer-mute-ramp
May 4, 2026
Merged

feat(audio-mixer): honor ramp_ms on mute toggles, with cancel-guard#540
srperens merged 1 commit into
mainfrom
feat/audio-mixer-mute-ramp

Conversation

@srperens

@srperens srperens commented May 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Mute toggles now honor ramp_ms like volume does — external callers (Open Live, MCP) can ask for broadcast-style fades on to_main_vol_N:mute etc. instead of being stuck on the hard-coded 10 ms anti-click.
  • Added a per-element generation counter so a fast unmute mid-fade cancels the pending tokio-scheduled mute=true. Without this, a 500 ms fade-out followed 40 ms later by an unmute would still let the stale mute=true land 460 ms later and silently kill the route.
  • UpdatePropertyRequest.ramp_ms doc and OpenAPI snapshot updated.

Usage

PATCH .../elements/<flow>:mixer:to_main_vol_<ch>
{ "property_name": "mute", "value": true,  "ramp_ms": 500 }   # 500 ms fade-out, then mute=true
{ "property_name": "mute", "value": false, "ramp_ms": 500 }   # mute=false, then 0→pre_mute fade-in

Test plan

  • cargo test --test volume_ramp_test (10/10, including 2 new)
    • long_mute_ramp_takes_full_duration — 200 ms fade-out keeps audio audible mid-ramp; mute lands only after full duration + grace
    • unmute_during_long_mute_fade_cancels_pending_toggle — unmute 40 ms into a 200 ms fade prevents stale mute=true from landing
  • cargo test --test pipeline_lifecycle_test (3/3)
  • cargo test --test openapi_test (snapshot regenerated; also picked up pending 0.4.12 → 0.4.13-dev version bump)
  • cargo build clean, clippy clean, fmt clean

🤖 Generated with Claude Code

Mute toggles previously hard-coded MUTE_ANTICLICK_RAMP_MS (10 ms), so
external callers (Open Live, MCP) sending PATCH `.../mute` could not
request the broadcast-style 500 ms route fade they need for on-air /
off-air transitions. The volume path already honored `ramp_ms`; this
brings parity to mute.

Backend:
- properties.rs: mute branch passes `ramp_ms.unwrap_or(MUTE_ANTICLICK_RAMP_MS)`
  into apply_mute, matching the volume branch.
- volume_ramp.rs: rename anticlick_ms -> ramp_ms in apply_mute. Add a
  per-element generation counter (Arc<Mutex<HashMap<String, u64>>>)
  so the tokio-scheduled `set_property("mute", true)` aborts on fire
  if a later apply_mute call has bumped the generation. Without this,
  a fast unmute mid-fade would still let the stale mute=true land
  afterwards and silently kill the route. clear() also bumps every
  generation so a stopped pipeline cannot be re-muted by leftover
  scheduled toggles.

API:
- UpdatePropertyRequest.ramp_ms doc now covers `mute` and points at the
  500 ms broadcast use case. OpenAPI snapshot regenerated (also picked
  up the pending 0.4.12 -> 0.4.13-dev version bump).

Tests:
- long_mute_ramp_takes_full_duration: 200 ms fade-out must keep
  audible level mid-ramp and only set mute=true after the full
  duration + grace.
- unmute_during_long_mute_fade_cancels_pending_toggle: regression for
  the cancel-guard — an unmute 40 ms into a 200 ms fade must prevent
  the stale mute=true from landing 200 ms later.
- All existing volume_ramp + pipeline_lifecycle tests still green.

Usage example: `PATCH .../elements/<flow>:mixer:to_main_vol_<ch>`
with `{ property_name: "mute", value: true, ramp_ms: 500 }` now
produces a 500 ms dB-linear fade-out before mute=true lands; the
matching `value: false, ramp_ms: 500` produces a 0->pre_mute fade-in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@srperens
srperens merged commit a5ec3f3 into main May 4, 2026
13 of 14 checks passed
@srperens
srperens deleted the feat/audio-mixer-mute-ramp branch May 4, 2026 15:40
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