grom: SoftSpoken "played a voice line over and over", and its **Silence NPC Voices**
option did nothing although it was switched on. Both are one deviation, and it is ours.
There is no Dialog channel to mute on 1.12, and SoftSpoken's own shim says so:
function SoundUtils:MuteChannel(channel, muted, fadeOut)
-- Intentionally unsupported on this client.
end
function SoundUtils:StopSound(clip, fadeMs)
SetCVar("MasterSoundEffects", 0)
SetCVar("MasterSoundEffects", 1)
end
That toggle is its whole repertoire, and it is not a hack the addon invented — the
reference makes it work on the **enabling** edge:
0x457500 the CVar callback: cl = !!value, tail-calls…
0x7a6570 …the flag store, edge-triggered (7a657f: je on an unchanged flag)
0x7a67a0 walks the live instance list; per node:
flags & 0x2 → skipped (the cinematic bypass)
flags & 0x4000008 → muted, not stopped (0x7a65a0)
otherwise, and ONLY when enabling (7a67eb: test cl,cl / je):
flags & 0x4 → 0x7a5950 stop
else → 0x7a4200 stop, then unlink (0x7604f0)
`0x7a5950` is unmistakable: it takes the node's stream handle (+0x34) and channel id
(+0x30) and calls the FMOD stop thunks. The *disabling* edge stops nothing at all —
sounds run on, gated silent at the pump, which is exactly what `category_amp` already
did. So we had half the CVar, and it was the half that does not matter. The comment on
`SoundConfig::enabled` claimed the two "sound the same"; they do for a player moving a
slider, and do not for an addon.
Both symptoms follow. `MuteGameDialogue` is gated on `AutoToggleDialog`, whose default is
`(Version.IsLegacyVanilla or …)` — true here, which is why his saved profile has no
`Audio` block and the option was on anyway — and on this client its body is nothing but
the toggle. The gate passed, the toggle ran, the NPC greeting played straight over the
line being read. The repeat is the same toggle spent on SoftSpoken's own clip:
`PlaySound` cuts *before* it plays ("On Vanilla, interrupt NPC audio BEFORE playback"),
and `StopKeeping` is documented as "silence the head but keep its place: it replays from
the start when Advance next reaches it, since the client has no seek". With the cut inert
the head never stopped, so the replay layered a second copy on a first still going.
`kit::cut_effects_on_reenable` now stops every `SoundCategory::Sfx` channel when the edge
is spent. Music and ambience are spared (the reference's mute-not-stop class) and
cinematic audio never enters `SoundOutput::channels` at all (the `flags & 0x2` bypass).
Two details are load-bearing, both pinned:
- **The edge is armed by the write, never sampled from the value.** The addon writes 0
and 1 inside one Lua call, so a system reading `SoundConfig::enabled` once a frame sees
`true` on both sides and no edge at all. `EffectCutArmed` is set in the CVar observer,
the one place that sees every write. It is its own resource rather than a `SoundConfig`
field because spending it needs `ResMut`, which as a field measured **43** new
ambiguous pairs against every `Res<SoundConfig>` reader in the frame.
- **The cut is spent between the Lua pass and the sound drain.** The reference stops
synchronously inside `SetCVar`, which is before the `PlaySoundFile` the addon calls
next. Applied after the drain it would stop the new clip instead of the old one — the
symptom inverted, not fixed.
The ceiling rises by 1, for the pair against the collider finisher: the same exclusive
`&mut World` shape `log_new_corpses` and `textinput::open_early` already record.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn