Actually fix audio sharing volume control

This commit is contained in:
Mitchell McCaffrey 2020-10-16 10:40:10 +11:00
parent e6d2fb5640
commit 1e11870171

View File

@ -6,12 +6,12 @@ import StreamMuteIcon from "../../icons/StreamMuteIcon";
import Banner from "../Banner"; import Banner from "../Banner";
function Stream({ stream, nickname }) { function Stream({ stream, nickname }) {
const [streamVolume, setStreamVolume] = useState(0); const [streamVolume, setStreamVolume] = useState(1);
const [showStreamInteractBanner, setShowStreamInteractBanner] = useState( const [showStreamInteractBanner, setShowStreamInteractBanner] = useState(
false false
); );
const [streamMuted, setStreamMuted] = useState(false);
const audioRef = useRef(); const audioRef = useRef();
const streamMuted = streamVolume === 0;
useEffect(() => { useEffect(() => {
if (audioRef.current) { if (audioRef.current) {
@ -21,11 +21,10 @@ function Stream({ stream, nickname }) {
.play() .play()
.then(() => { .then(() => {
// Played fine // Played fine
setStreamVolume(1);
}) })
.catch(() => { .catch(() => {
// Unable to autoplay // Unable to autoplay
setStreamVolume(0); setStreamMuted(true);
setShowStreamInteractBanner(true); setShowStreamInteractBanner(true);
}); });
} }
@ -35,11 +34,11 @@ function Stream({ stream, nickname }) {
if (audioRef.current) { if (audioRef.current) {
if (streamMuted) { if (streamMuted) {
audioRef.current.play().then(() => { audioRef.current.play().then(() => {
setStreamVolume(1); setStreamMuted(false);
setShowStreamInteractBanner(false); setShowStreamInteractBanner(false);
}); });
} else { } else {
setStreamVolume(0); setStreamMuted(true);
} }
} }
} }
@ -47,40 +46,50 @@ function Stream({ stream, nickname }) {
function handleVolumeChange(event) { function handleVolumeChange(event) {
const volume = parseFloat(event.target.value); const volume = parseFloat(event.target.value);
setStreamVolume(volume); setStreamVolume(volume);
if (showStreamInteractBanner) {
audioRef.current.play().then(() => {
setShowStreamInteractBanner(false);
});
}
} }
// Use an audio context gain node to control volume to go past 100%
const audioGainRef = useRef();
useEffect(() => {
if (stream && !streamMuted) {
let audioContext = new AudioContext();
let source = audioContext.createMediaStreamSource(stream);
let gainNode = audioContext.createGain();
gainNode.gain.value = 0;
source.connect(gainNode);
gainNode.connect(audioContext.destination);
audioGainRef.current = gainNode;
}
}, [stream, streamMuted]);
// Platforms like iOS don't allow you to control audio volume // Platforms like iOS don't allow you to control audio volume
// Detect this by trying to change the audio volume // Detect this by trying to change the audio volume
const [isVolumeControlAvailable, setIsVolumeControlAvailable] = useState( const [isVolumeControlAvailable, setIsVolumeControlAvailable] = useState(
true true
); );
useEffect(() => { useEffect(() => {
if (audioRef.current) { let audio = audioRef.current;
const prevVolume = audioRef.current.volume; function checkVolumeControlAvailable() {
audioRef.current.volume = 0.5; const prevVolume = audio.volume;
setIsVolumeControlAvailable(audioRef.current.volume === 0.5); // Set volume to 0.5, then check if the value actually stuck 100ms later
audioRef.current.volume = prevVolume; audio.volume = 0.5;
setTimeout(() => {
setIsVolumeControlAvailable(audio.volume === 0.5);
audio.volume = prevVolume;
}, [100]);
} }
}, [stream]);
audio.addEventListener("playing", checkVolumeControlAvailable);
return () => {
audio.removeEventListener("playing", checkVolumeControlAvailable);
};
}, []);
// Use an audio context gain node to control volume to go past 100%
const audioGainRef = useRef();
useEffect(() => {
let audioContext;
if (stream && !streamMuted && isVolumeControlAvailable) {
audioContext = new AudioContext();
let source = audioContext.createMediaStreamSource(stream);
let gainNode = audioContext.createGain();
gainNode.gain.value = 0;
source.connect(gainNode);
gainNode.connect(audioContext.destination);
audioGainRef.current = gainNode;
}
return () => {
audioContext && audioContext.close();
};
}, [stream, streamMuted, isVolumeControlAvailable]);
useEffect(() => { useEffect(() => {
if (audioGainRef.current && audioRef.current) { if (audioGainRef.current && audioRef.current) {
@ -109,12 +118,12 @@ function Stream({ stream, nickname }) {
<StreamMuteIcon muted={streamMuted} /> <StreamMuteIcon muted={streamMuted} />
</IconButton> </IconButton>
<Slider <Slider
value={streamVolume} value={streamMuted ? 0 : streamVolume}
min={0} min={0}
max={2} max={2}
step={0.1} step={0.1}
onChange={handleVolumeChange} onChange={handleVolumeChange}
disabled={!isVolumeControlAvailable} disabled={!isVolumeControlAvailable || streamMuted}
/> />
{stream && <audio ref={audioRef} playsInline muted={streamMuted} />} {stream && <audio ref={audioRef} playsInline muted={streamMuted} />}
</Flex> </Flex>