@fishjam-cloud/react-client
Version:
React client library for Fishjam
60 lines (59 loc) • 2.75 kB
JavaScript
import { useContext, useEffect, useMemo, useState } from "react";
import { FishjamClientStateContext } from "../contexts/fishjamState";
/**
* Voice activity detection. Use this hook to check if voice is detected in audio track for given peer(s).
*
* Example usage:
* ```tsx
* function WhoIsTalkingComponent({ peerIds }: { peerIds: PeerId[] }) {
* const peersInfo = useVAD({peerIds});
* const activePeers = (Object.keys(peersInfo) as PeerId[]).filter((peerId) => peersInfo[peerId]);
*
* return `Now talking: ${activePeers.join(", ")}`;
* }
* ```
* @param peerIds List of ids of peers to subscribe to for voice activity detection notifications.
* @category Connection
* @group Hooks
* @returns Each key is a peerId and the boolean value indicates if voice activity is currently detected for that peer.
*/
export const useVAD = ({ peerIds }) => {
const clientState = useContext(FishjamClientStateContext);
if (!clientState)
throw Error("useVAD must be used within FishjamProvider");
const micTracksWithSelectedPeerIds = useMemo(() => Object.values(clientState.peers)
.filter((peer) => peerIds.includes(peer.id))
.map((peer) => ({
peerId: peer.id,
microphoneTracks: Array.from(peer.tracks.values()).filter(({ metadata }) => metadata?.type === "microphone"),
})), [clientState.peers, peerIds]);
const getDefaultVadStatuses = () => micTracksWithSelectedPeerIds.reduce((mappedTracks, peer) => ({
...mappedTracks,
[peer.peerId]: peer.microphoneTracks.reduce((vadStatuses, track) => ({ ...vadStatuses, [track.trackId]: track.vadStatus }), {}),
}), {});
const [_vadStatuses, setVadStatuses] = useState(getDefaultVadStatuses);
useEffect(() => {
const unsubs = micTracksWithSelectedPeerIds.map(({ peerId, microphoneTracks }) => {
const updateVadStatus = (track) => {
setVadStatuses((prev) => ({
...prev,
[peerId]: { ...prev[peerId], [track.trackId]: track.vadStatus },
}));
};
microphoneTracks.forEach((track) => {
track.on("voiceActivityChanged", updateVadStatus);
});
return () => {
microphoneTracks.forEach((track) => {
track.off("voiceActivityChanged", updateVadStatus);
});
};
});
return () => unsubs.forEach((unsub) => unsub());
}, [micTracksWithSelectedPeerIds]);
const vadStatuses = useMemo(() => Object.fromEntries(Object.entries(_vadStatuses).map(([peerId, tracks]) => [
peerId,
Object.values(tracks).some((vad) => vad === "speech"),
])), [_vadStatuses]);
return vadStatuses;
};