(feat): improved microphone gate
(fix): weird behaviour related to users that are screensharing
This commit is contained in:
parent
e76c579bb4
commit
c795f691bf
5 changed files with 34 additions and 9 deletions
|
|
@ -152,12 +152,12 @@ export default function Base({
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
if (view === "grid") {
|
if (view === "grid") {
|
||||||
focusParticipant(user.user_id);
|
focusParticipant(user.user_id, type);
|
||||||
} else {
|
} else {
|
||||||
if (user.user_id === focusedParticipantId) {
|
if (user.user_id === focusedParticipantId) {
|
||||||
setCallView("grid");
|
setCallView("grid");
|
||||||
} else {
|
} else {
|
||||||
focusParticipant(user.user_id);
|
focusParticipant(user.user_id, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -215,7 +215,7 @@ export default function Base({
|
||||||
ref={currentCard}
|
ref={currentCard}
|
||||||
className={`transition-all duration-150 z-10 bg-card absolute top-0 left-0 w-full h-full flex gap-2 items-center justify-center ${
|
className={`transition-all duration-150 z-10 bg-card absolute top-0 left-0 w-full h-full flex gap-2 items-center justify-center ${
|
||||||
flush ? "rounded-none" : "rounded-sm"
|
flush ? "rounded-none" : "rounded-sm"
|
||||||
} ${isSpeaking ? "border-4 border-(--primary-foreground-alt)/75" : "border-0"}`}
|
} ${type === "user" && isSpeaking ? "border-4 border-(--primary-foreground-alt)/75" : "border-0"}`}
|
||||||
style={{ containerType: "size" }}
|
style={{ containerType: "size" }}
|
||||||
>
|
>
|
||||||
{/* Detect video / user and place here */}
|
{/* Detect video / user and place here */}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
import { log } from "@tensamin/shared/log";
|
||||||
import { useCall } from "./store";
|
import { useCall } from "./store";
|
||||||
|
|
||||||
const SPEAKING_THRESHOLD = 0.01;
|
const SPEAKING_THRESHOLD = 0.01;
|
||||||
const SPEAKING_HANGTIME_MS = 250;
|
const SPEAKING_HANGTIME_MS = 500;
|
||||||
const ANALYSIS_INTERVAL_MS = 30;
|
const ANALYSIS_INTERVAL_MS = 30;
|
||||||
const FFT_SIZE = 256;
|
const FFT_SIZE = 256;
|
||||||
|
|
||||||
|
|
@ -137,8 +138,10 @@ class SpeakingDetector {
|
||||||
const db = 20 * Math.log10(Math.max(rms, 0.0001));
|
const db = 20 * Math.log10(Math.max(rms, 0.0001));
|
||||||
|
|
||||||
if (!this.localMicGateClosed && db < this.gateThresholdStart) {
|
if (!this.localMicGateClosed && db < this.gateThresholdStart) {
|
||||||
|
log(3, "noise gate", "purple", "closed");
|
||||||
this.muteLocalTrack(true);
|
this.muteLocalTrack(true);
|
||||||
} else if (this.localMicGateClosed && db > this.gateThresholdEnd) {
|
} else if (this.localMicGateClosed && db > this.gateThresholdEnd) {
|
||||||
|
log(3, "noise gate", "purple", "opened");
|
||||||
this.muteLocalTrack(false);
|
this.muteLocalTrack(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ type CallStore = {
|
||||||
screenShareEnabled: boolean;
|
screenShareEnabled: boolean;
|
||||||
screenShareSession: ScreenShareSession | null;
|
screenShareSession: ScreenShareSession | null;
|
||||||
focusedParticipantId: number | null;
|
focusedParticipantId: number | null;
|
||||||
|
focusedParticipantType: "user" | "stream" | null;
|
||||||
usersInFocusedViewHidden: boolean;
|
usersInFocusedViewHidden: boolean;
|
||||||
watchedStreamParticipantIds: number[];
|
watchedStreamParticipantIds: number[];
|
||||||
pendingWatchedParticipantIds: number[];
|
pendingWatchedParticipantIds: number[];
|
||||||
|
|
@ -464,6 +465,8 @@ function syncScreenShareParticipants() {
|
||||||
watchedStreamParticipantIds,
|
watchedStreamParticipantIds,
|
||||||
pendingWatchedParticipantIds,
|
pendingWatchedParticipantIds,
|
||||||
focusedParticipantId,
|
focusedParticipantId,
|
||||||
|
focusedParticipantType:
|
||||||
|
focusedParticipantId == null ? null : state.focusedParticipantType,
|
||||||
view:
|
view:
|
||||||
state.view === "focused" && focusedParticipantId == null
|
state.view === "focused" && focusedParticipantId == null
|
||||||
? "grid"
|
? "grid"
|
||||||
|
|
@ -634,6 +637,7 @@ export function startWatchingStream(participantId: number) {
|
||||||
? state.pendingWatchedParticipantIds
|
? state.pendingWatchedParticipantIds
|
||||||
: [...state.pendingWatchedParticipantIds, participantId],
|
: [...state.pendingWatchedParticipantIds, participantId],
|
||||||
focusedParticipantId: participantId,
|
focusedParticipantId: participantId,
|
||||||
|
focusedParticipantType: "stream",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -653,9 +657,13 @@ export function setParticipantTrackSubscribed(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus a participant in the main call view even when they are not sharing a screen.
|
// Focus a participant in the main call view even when they are not sharing a screen.
|
||||||
export function focusParticipant(participantId: number) {
|
export function focusParticipant(
|
||||||
|
participantId: number,
|
||||||
|
type: "user" | "stream" = "user",
|
||||||
|
) {
|
||||||
useCall.setState({
|
useCall.setState({
|
||||||
focusedParticipantId: participantId,
|
focusedParticipantId: participantId,
|
||||||
|
focusedParticipantType: type,
|
||||||
view: "focused",
|
view: "focused",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -675,6 +683,10 @@ export function stopWatchingStream(participantId: number) {
|
||||||
state.focusedParticipantId === participantId
|
state.focusedParticipantId === participantId
|
||||||
? null
|
? null
|
||||||
: state.focusedParticipantId,
|
: state.focusedParticipantId,
|
||||||
|
focusedParticipantType:
|
||||||
|
state.focusedParticipantId === participantId
|
||||||
|
? null
|
||||||
|
: state.focusedParticipantType,
|
||||||
view:
|
view:
|
||||||
state.view === "focused" && state.focusedParticipantId === participantId
|
state.view === "focused" && state.focusedParticipantId === participantId
|
||||||
? "grid"
|
? "grid"
|
||||||
|
|
@ -790,6 +802,7 @@ export async function disconnect() {
|
||||||
view: "preview",
|
view: "preview",
|
||||||
screenShareSession: null,
|
screenShareSession: null,
|
||||||
focusedParticipantId: null,
|
focusedParticipantId: null,
|
||||||
|
focusedParticipantType: null,
|
||||||
usersInFocusedViewHidden: false,
|
usersInFocusedViewHidden: false,
|
||||||
watchedStreamParticipantIds: [],
|
watchedStreamParticipantIds: [],
|
||||||
pendingWatchedParticipantIds: [],
|
pendingWatchedParticipantIds: [],
|
||||||
|
|
@ -950,6 +963,7 @@ export function resetCallState() {
|
||||||
deaf: false,
|
deaf: false,
|
||||||
screenShareSession: null,
|
screenShareSession: null,
|
||||||
focusedParticipantId: null,
|
focusedParticipantId: null,
|
||||||
|
focusedParticipantType: null,
|
||||||
usersInFocusedViewHidden: false,
|
usersInFocusedViewHidden: false,
|
||||||
watchedStreamParticipantIds: [],
|
watchedStreamParticipantIds: [],
|
||||||
pendingWatchedParticipantIds: [],
|
pendingWatchedParticipantIds: [],
|
||||||
|
|
@ -1002,6 +1016,7 @@ export const useCall = create<CallStore>(() => ({
|
||||||
screenShareEnabled: room.localParticipant.isScreenShareEnabled,
|
screenShareEnabled: room.localParticipant.isScreenShareEnabled,
|
||||||
screenShareSession: null,
|
screenShareSession: null,
|
||||||
focusedParticipantId: null,
|
focusedParticipantId: null,
|
||||||
|
focusedParticipantType: null,
|
||||||
usersInFocusedViewHidden: false,
|
usersInFocusedViewHidden: false,
|
||||||
watchedStreamParticipantIds: [],
|
watchedStreamParticipantIds: [],
|
||||||
pendingWatchedParticipantIds: [],
|
pendingWatchedParticipantIds: [],
|
||||||
|
|
@ -1038,7 +1053,7 @@ export function useInitializeCall() {
|
||||||
new DeepFilterNoiseFilterProcessor({
|
new DeepFilterNoiseFilterProcessor({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
enableNoiseReduction: true,
|
enableNoiseReduction: true,
|
||||||
noiseReductionLevel: 80,
|
noiseReductionLevel: 60,
|
||||||
sampleRate: 48000,
|
sampleRate: 48000,
|
||||||
assetConfig: {
|
assetConfig: {
|
||||||
cdnUrl: "/assets",
|
cdnUrl: "/assets",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ export default function View() {
|
||||||
const callIsFullscreen = useCall((state) => state.callIsFullscreen);
|
const callIsFullscreen = useCall((state) => state.callIsFullscreen);
|
||||||
|
|
||||||
const focusedParticipantId = useCall((state) => state.focusedParticipantId);
|
const focusedParticipantId = useCall((state) => state.focusedParticipantId);
|
||||||
|
const focusedParticipantType = useCall(
|
||||||
|
(state) => state.focusedParticipantType,
|
||||||
|
);
|
||||||
const activeScreenShareParticipantIds = useCall(
|
const activeScreenShareParticipantIds = useCall(
|
||||||
(state) => state.activeScreenShareParticipantIds,
|
(state) => state.activeScreenShareParticipantIds,
|
||||||
);
|
);
|
||||||
|
|
@ -157,6 +160,10 @@ export default function View() {
|
||||||
const focusedParticipant = getParticipantById(focusedParticipantId);
|
const focusedParticipant = getParticipantById(focusedParticipantId);
|
||||||
const focusedParticipantHasActiveScreenShare =
|
const focusedParticipantHasActiveScreenShare =
|
||||||
activeScreenShareParticipantIdSet.has(focusedParticipantId);
|
activeScreenShareParticipantIdSet.has(focusedParticipantId);
|
||||||
|
const focusedTileType: "user" | "stream" =
|
||||||
|
focusedParticipantType === "stream" && focusedParticipantHasActiveScreenShare
|
||||||
|
? "stream"
|
||||||
|
: "user";
|
||||||
const isImmersiveFocusedView = callIsFullscreen && usersInFocusedViewHidden;
|
const isImmersiveFocusedView = callIsFullscreen && usersInFocusedViewHidden;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -179,7 +186,7 @@ export default function View() {
|
||||||
<Base
|
<Base
|
||||||
fill={isImmersiveFocusedView}
|
fill={isImmersiveFocusedView}
|
||||||
flush={isImmersiveFocusedView || isFocusedTileFlush}
|
flush={isImmersiveFocusedView || isFocusedTileFlush}
|
||||||
type={focusedParticipantHasActiveScreenShare ? "stream" : "user"}
|
type={focusedTileType}
|
||||||
participant={focusedParticipant}
|
participant={focusedParticipant}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,6 @@ export const storageDefaults: Storage = {
|
||||||
cached_contacts: [],
|
cached_contacts: [],
|
||||||
cached_communities: [],
|
cached_communities: [],
|
||||||
ttp_url: "https://tensamin.net:959",
|
ttp_url: "https://tensamin.net:959",
|
||||||
call_mute_range_start: -50,
|
call_mute_range_start: -55,
|
||||||
call_mute_range_end: -40,
|
call_mute_range_end: -45,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue