import { convertTimeToTag } from "../lrc-parser.js"; import { AudioActionType, audioRef, AudioState, audioStatePubSub, currentTimePubSub } from "../utils/audiomodule.js"; import { loadAudioDialogRef } from "./loadaudio.js"; import { Forward5sSVG, LoadAudioSVG, PauseSVG, PlaySVG, Replay5sSVG } from "./svg.js"; const { useState, useEffect, useRef, useCallback, useMemo } = React; interface ISliderProps { min: number; max: number; step?: string | number; value: number; onInput: (event: React.ChangeEvent) => void; className: string; } const Slider: React.FC = ({ min, max, step, value, onInput, className }) => { const total = max - min || 1; const percent = (value - min) / total; return (
); }; const TimeLine: React.FC<{ duration: number; paused: boolean }> = ({ duration, paused }) => { const self = useRef(Symbol(TimeLine.name)); const [currentTime, setCurrentTime] = useState(audioRef.currentTime); const [rate, setRate] = useState(audioRef.playbackRate); useEffect(() => { return audioStatePubSub.sub(self.current, (data) => { if (data.type === AudioActionType.rateChange) { setRate(data.payload); } }); }, []); useEffect(() => { if (paused) { // paused but user changing the time return currentTimePubSub.sub(self.current, (data) => { setCurrentTime(data); }); } else { const id = setInterval(() => { setCurrentTime(audioRef.currentTime); }, 500 / rate); return (): void => { clearInterval(id); }; } }, [paused, rate]); const rafId = useRef(0); const onInput = useCallback((ev: React.ChangeEvent) => { if (rafId.current) { cancelAnimationFrame(rafId.current); } const value = ev.target.value; rafId.current = requestAnimationFrame(() => { const time = Number.parseInt(value, 0); setCurrentTime(time); audioRef.currentTime = time; }); }, []); const durationTimeTag = useMemo(() => { return duration ? " / " + convertTimeToTag(duration, 0, false) : false; }, [duration]); return ( <> ); }; const RateSlider: React.FC<{ lang: Language }> = ({ lang }) => { const self = useRef(Symbol(RateSlider.name)); const [playbackRate, setPlaybackRate] = useState(audioRef.playbackRate); useEffect(() => { return audioStatePubSub.sub(self.current, (data: AudioState) => { if (data.type === AudioActionType.rateChange) { setPlaybackRate(data.payload); } }); }, []); // playbackRate: [1/e, e] // playbackRateSliderValue: [-1, 1] // playbackRateSliderValue === ln(playbackRate) // playbackRate === exp(playbackRateSliderValue) const playbackRateSliderValue = useMemo(() => { return Math.log(playbackRate); }, [playbackRate]); const onPlaybackRateChange = useCallback((ev: React.ChangeEvent) => { const value = Math.exp(Number.parseFloat(ev.target.value)); setPlaybackRate(value); audioRef.playbackRate = value; }, []); const onPlaybackRateReset = useCallback(() => { audioRef.playbackRate = 1; }, []); return ( <> ); }; export const LrcAudio: React.FC<{ lang: Language }> = ({ lang }) => { const self = useRef(Symbol(LrcAudio.name)); const [paused, setPaused] = useState(audioRef.paused); const [duration, setDuration] = useState(audioRef.duration); useEffect(() => { return audioStatePubSub.sub(self.current, (data: AudioState) => { switch (data.type) { case AudioActionType.getDuration: { setDuration(data.payload); setPaused(audioRef.paused); break; } case AudioActionType.pause: { setPaused(data.payload); break; } } }); }, []); const onReplay5s = useCallback((ev: React.MouseEvent) => { audioRef.step(ev, -5); }, []); const onForward5s = useCallback((ev: React.MouseEvent) => { audioRef.step(ev, 5); }, []); const onPlayPauseToggle = useCallback(() => { audioRef.toggle(); }, []); const onLoadAudioButtonClick = useCallback(() => { loadAudioDialogRef.open(); }, []); return (
); };