feat(call): per-participant volume up to 200% via WebAudio gain
HTMLMediaElement.volume caps at 1.0, so boosting a quiet peer past 100% needs an explicit GainNode in the output chain. New remoteAudioPipelines module owns one AudioContext + GainNode per remote audio track; attachTrack / detachTrack now create and tear down the pipeline alongside the LiveKit element. Once a track is on the WebAudio path its direct output is diverted (createMediaElementSource semantics), so audio.muted / volume can't drive output anymore. Deafen, watch-state, manual screen-share mute and per-user volume are collapsed into one effective-gain formula that gets recomputed on every state flip — the effect subscribes to both participantVolumes and screenShareVolumes for live slider drags. Slider ranges updated to 0–200% across: - ParticipantVolumeMenu (per-user right-click menu) - ParticipantsPopover (in-call participant list) - ScreenShareContextMenu (per-share right-click) Values above 100% render the percentage in amber as a soft hint that clipping is possible. Clamp in both volume stores extended to [0, 2] so persisted values survive. setAudioOutputDevice now additionally routes via AudioContext.setSinkId (Chrome 115+) for the WebAudio graph; the HTMLAudioElement.setSinkId fallback stays for older runtimes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,10 @@
|
||||
// with how the context menu surfaces the control ("Dennis's share").
|
||||
|
||||
const DEFAULT_VOLUME = 1;
|
||||
// Matches Discord's slider range — up to 200% via the WebAudio GainNode in
|
||||
// remoteAudioPipelines. HTMLMediaElement.volume only goes to 1.0, so the
|
||||
// above-100% values are only meaningful on the WebAudio output path.
|
||||
const MAX_VOLUME = 2;
|
||||
|
||||
type VolumeMap = Record<string, number>;
|
||||
type Listener = (map: VolumeMap) => void;
|
||||
@@ -18,7 +22,7 @@ const listeners = new Set<Listener>();
|
||||
function clamp(v: number): number {
|
||||
if (!Number.isFinite(v)) return DEFAULT_VOLUME;
|
||||
if (v < 0) return 0;
|
||||
if (v > 1) return 1;
|
||||
if (v > MAX_VOLUME) return MAX_VOLUME;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user