Skip to content

MCMP effects: environment chains, inline effects, live automation#35

Merged
ctoth merged 5 commits into
masterfrom
feat/mcmp-effects
Jun 4, 2026
Merged

MCMP effects: environment chains, inline effects, live automation#35
ctoth merged 5 commits into
masterfrom
feat/mcmp-effects

Conversation

@ctoth
Copy link
Copy Markdown
Contributor

@ctoth ctoth commented Jun 4, 2026

What

Adds an effects layer to the client's MCMP (Client.Media) handler so the server can place sounds in acoustic environments, apply per-sound effects, and automate them live. Built on Cacophony 0.25's effects API (bus filter chains, drain-based rerouting, parameter ramps, bypass).

Three capabilities, gated behind a capability handshake so a server only uses what this client advertises:

  • Environment chains — named, reusable effect chains (e.g. a "cave" reverb). Sounds route into a chain by name; removing a chain cleanly reroutes its live sounds back to master.
  • Inline effects — a per-sound effect list on Play/Update, with optional bypass, for one-off treatments without a named chain.
  • Live automation — ramp an effect parameter to a target value over a duration (e.g. open a filter, swell reverb mix).

Wire format

The wire vocabulary is engine-neutral — intent names and real units (seconds, Hz, dB, 0–1 mix), not Cacophony's internal option names — so the protocol isn't welded to one audio engine. A pure resolveEffect translates wire specs into Cacophony factory options; unknown effect types and unknown params degrade to no-ops rather than erroring.

Capability payload (Client.Media.EffectsSupport, client → server) advertises supported effect types, reverb algorithms, built-in presets, automation/chain support, and limits (max chains, max effects/chain).

Capability handshake

On GMCP negotiation the client sends Client.Media.EffectsSupport right after login, so effects are strictly opt-in per client.

New messages

  • Client.Media.Chain / Client.Media.ChainStop — create/replace/remove a named chain
  • Client.Media.Automate — ramp a parameter on a chain or inline effect
  • chain / send / effects keys added to Client.Media.Play and Update

Notes

  • Named-chain routing on ambisonic (3D) sounds is intentionally unsupported (the ambisonic renderer's output edge isn't bus-tracked, so chain teardown can't reroute it) — it warns. Inline effects on ambisonic sounds do work.
  • Bumps cacophony ^0.23.0^0.25.1 (the effects API).

Tests

New unit suites for resolveEffect, buildEffect, EffectChain, MediaEffects, plus extended Client.Media and AmbisonicRenderer coverage (routing, inline, automation, ambisonic→inline). Full src suite passes; typecheck clean.

ctoth and others added 5 commits June 3, 2026 17:03
…ity)

Engine-neutral effect vocabulary translated to cacophony 0.25.1:
- resolveEffect: pure wire-spec -> cacophony plan (algorithm dispatch, unit
  conversion, string modes, clamping, unknown-drop) + per-effect/chain presets
- buildEffect: dynamic factory dispatch boundary
- EffectChain: bus owner; master non-owning overlay (V2); named-chain teardown
  reroutes live sounds via drainTo (V3); degradation skips unknown/failed effects
- MediaEffects: chain map + caps + EffectsSupport capability payload
- Client.Media: chain/send keys on Play/Update, handleChain/handleChainStop,
  applyChainRouting (ambisonic guard V11, dry-on-missing), sendEffectsSupport
  advertised on GMCP negotiation

Bumps cacophony ^0.23 -> ^0.25.1 (the prep primitives). 80 new unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
P1 — inline per-sound effect chains:
- EffectChain.createAnonymous (unnamed bus) + connectDownstream for serial
  sound -> inline -> named chain -> master composition
- Play/Update 'effects' key builds an anon chain routed on the sound; torn down
  in releaseSound BEFORE the cleaned-sounds guard (V4), with the stop-all branch
  funneled through releaseSound so no inline bus leaks
- generation token discards a build whose sound was released mid-await (V5)

P2 — automation + bypass:
- Client.Media.Automate: ramp an effect's params (by chain or media key + effect
  id/index) reusing resolveEffect to translate wire->cacophony param names, then
  bus.rampFilterParam; liveness-guarded against a destroyed bus (V6)
- bypass via cacophony's setFilterBypassed primitive (keeps node+params alive);
  honored both at build (EffectSpec.bypass) and at runtime (Automate bypass)
- capability now advertises automation:true

+11 unit tests (EffectChain automate/bypass/anonymous/downstream; Media inline
routing, V4 teardown funnel, automate-by-key, bypass-by-key).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Expanded preset library: per-effect presets across reverb/distortion/delay/
  chorus/flanger/phaser/tremolo/compressor/gate, and chain presets (room,
  stadium, vinyl, ghost, drunk, brokenSpeaker, …) — engine-neutral data,
  auto-advertised via the capability's presets list.
- Ambisonic rework (V11): AmbisonicRenderer.attachPlayback takes an optional
  output target, so the binaural decode output is no longer hard-wired to master.
  Inline effects now work on ambisonic sounds (renderer output → inline bus →
  master), torn down with the sound. Named-chain routing stays unsupported for
  ambisonic (the renderer's manual output edge is not tracked by the bus, so
  chain-removal could not reroute it) — warned, not silently broken.
- Refactored the inline path into buildInlineChain (build/store) + applyInlineEffects
  (build + route); applyEffectRouting defers ambisonic to the renderer path.

+2 tests (renderer output target; ambisonic → inline routing). 467 src tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert an unintended whole-file reformat and use the typed
requireGMCPPackage('Client.Media').sendEffectsSupport() call instead of an
inline-cast guard. Net diff vs master is now the two-line capability trigger.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Keep package.json homepage as-is; this branch should only carry the
cacophony bump and the effects layer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ctoth ctoth merged commit a5e4f48 into master Jun 4, 2026
1 check passed
github-actions Bot pushed a commit that referenced this pull request Jun 4, 2026
MCMP effects: environment chains, inline effects, live automation a5e4f48
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