import React, { useCallback, useEffect, useRef, useState } from 'react';
import useEventListener from './useEventListener';
interface UseFadeAudioProps {
audioRef: React.RefObject<HTMLAudioElement>;
cloneAudioRef: React.RefObject<HTMLAudioElement>;
startFade: boolean;
paused: boolean;
}
const TIMING = 10;
const useFadeAudio = ({ audioRef, cloneAudioRef, startFade, paused }: UseFadeAudioProps) => {
const [isStartFadeClone, setIsStartFadeClone] = useState<boolean>(false);
const currentTimeRef = useRef<number>(0);
const cloneCurrentTimeRef = useRef<number>(0);
const timerIdRef = useRef<number>(0);
const cloneTimerIdRef = useRef<number>(0);
const canStartAudio1Ref = useRef<boolean>(true);
const canStartAudio2Ref = useRef<boolean>(true);
const fadeAudio = useCallback(
(
audio: HTMLAudioElement,
nextAudio: HTMLAudioElement,
isCloneAudio: boolean,
canStartCurrentAudioRef: React.MutableRefObject<boolean>,
canStartNextAudioRef: React.MutableRefObject<boolean>,
) => {
const { duration } = audio;
const startPoint = 50;
const startRange = (duration * startPoint) / 100; // ex: duration = 10s, startPoint = 50% => startRange = 5s
const fadeStart = (startRange * 1000) / TIMING;
const timerId = window.setInterval(() => {
const currentTime = !isCloneAudio ? currentTimeRef.current : cloneCurrentTimeRef.current;
// Start fade in for next audio
if (duration - currentTime <= startRange && canStartCurrentAudioRef.current) {
nextAudio.volume = 0;
nextAudio.play();
cloneCurrentTimeRef.current = 0;
canStartCurrentAudioRef.current = !canStartCurrentAudioRef.current; // Prevent
canStartNextAudioRef.current = true;
if (!isCloneAudio) setIsStartFadeClone(true);
}
// Fade in
if (audio.volume < 1 && duration - currentTime > startRange) {
const newVolume = audio.volume + 1 / fadeStart;
console.log('FADE In');
if (newVolume >= 1) audio.volume = 1;
else audio.volume = newVolume;
return;
}
// Fade out
if (audio.volume > 0 && duration - currentTime <= startRange) {
const newVolume = audio.volume - 1 / fadeStart;
if (newVolume <= 0) audio.volume = 0;
else audio.volume = newVolume;
}
console.log('PAUSED', timerId);
}, TIMING);
return timerId;
},
[],
);
useEffect(() => {
if (paused) {
clearInterval(timerIdRef.current);
clearInterval(cloneTimerIdRef.current);
}
}, [paused]);
useEffect(() => {
const audio = audioRef.current;
const nextAudio = cloneAudioRef.current;
if (!audio || !nextAudio || !startFade) return;
timerIdRef.current = fadeAudio(audio, nextAudio, false, canStartAudio2Ref, canStartAudio1Ref);
return () => clearInterval(timerIdRef.current);
}, [audioRef, cloneAudioRef, startFade, paused, fadeAudio]);
useEffect(() => {
const audio = cloneAudioRef.current;
const nextAudio = audioRef.current;
if (!audio || !nextAudio || !isStartFadeClone) return;
cloneTimerIdRef.current = fadeAudio(
audio,
nextAudio,
true,
canStartAudio1Ref,
canStartAudio2Ref,
);
return () => clearInterval(cloneTimerIdRef.current);
}, [audioRef, cloneAudioRef, paused, isStartFadeClone, fadeAudio]);
useEventListener(
'timeupdate',
() => {
currentTimeRef.current = audioRef.current?.currentTime ?? 0;
},
audioRef,
);
useEventListener(
'timeupdate',
() => {
cloneCurrentTimeRef.current = cloneAudioRef.current?.currentTime ?? 0;
},
cloneAudioRef,
);
};
export default useFadeAudio;