apply() guarded on mic.getProcessor(), which is only set after setProcessor()
resolves (after the whole wasm/model load), so mic-published followed by
camera-published constructed two processors — two AudioContexts, two model
loads, double lock hold time. Track the in-flight processor per room, skip
apply() while one is pending, and destroy a pending processor if the module
is torn down before setProcessor resolves. Unit-tested.
Fixes#10
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
On a failed restart() the processor set processedTrack to the LiveKit-owned
input track. LiveKit's internalStopProcessor() stops processedTrack and then
republishes _mediaStreamTrack — the same object — so any later
stopProcessor()/teardown killed the live mic for the rest of the session.
Leave processedTrack undefined so LiveKit falls through to its own track.
Unit-tested.
Fixes#2
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
Deafen wrote RemoteParticipant.setVolume(0, Microphone), which EC's own
createVolumeControls overwrote with the tile volume every time sink$
re-emitted (every join / re-resolution) — so anyone joining while you were
deafened was audible — and undeafen blanket-wrote 1 to every participant,
clobbering per-tile volume/mute. The ParticipantConnected listeners also
hung off livekitRoomItems$, which is empty while alone, so the first joiner
could be heard briefly.
Deafen now drives setAudioEnabled$ -> muteAllAudio$, the `muted` prop
InCallView already passes to every audio renderer, which also silences
Track.Source.Unknown soundboard audio (the known P6-2 gap). Undeafen
restores the user's own output state and never touches the mute-all
setting. Re-applying the same state is a no-op so resendForkState() is
safe. Screenshare-audio-only mute keeps setVolume(ScreenShareAudio) but
only restores participants it muted itself. Rooms come from
allConnections$ like the sibling modules. Unit-tested.
Fixes#1
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This is the mode in which we sent membership events with the 'oldest membership' transport selection algorithm, which stopped being the default back in version 0.21.0. Users will no longer be able to select this mode in developer settings, and admins will no longer be able to select legacy mode through the config either. The app will still continue to support *receiving* membership events with the 'oldest membership' transport selection algorithm from others, however.
Support for events with an array of memberships from different devices was removed over a year ago in matrix-js-sdk ffd3c9575e9def576739baf6b1dc329b0db55c0c.