import React, { useMemo, useEffect, useState, useCallback } from "react"; import * as Actions from "../actionCreators"; import * as Selectors from "../selectors"; import { VISUALIZERS, MEDIA_STATUS } from "../constants"; import { preRenderBg, usePaintOscilloscope, PIXEL_DENSITY, usePaintBars, usePaintDummyBarFrame, } from "./visualizerUtils"; import { useAnimationLoop, useTypedSelector, useActionCreator } from "../hooks"; type Props = { analyser: AnalyserNode; }; export default function Visualizer({ analyser }: Props) { const windowShade = useTypedSelector(Selectors.getWindowShade)("main")!; const renderWidth = windowShade ? 38 : 76; const renderHeight = windowShade ? 5 : 16; const colors = useTypedSelector(Selectors.getSkinColors); const style = useTypedSelector(Selectors.getVisualizerStyle); const status = useTypedSelector(Selectors.getMediaStatus); const dummyVizData = useTypedSelector(Selectors.getDummyVizData); const toggleVisualizerStyle = useActionCreator(Actions.toggleVisualizerStyle); const [canvas, setCanvas] = useState(null); const height = renderHeight * PIXEL_DENSITY; const width = renderWidth * PIXEL_DENSITY; const canvasCtx = useMemo(() => { return canvas?.getContext("2d") ?? null; }, [canvas]); const bgCanvas = useMemo(() => { return preRenderBg(width, height, colors[0], colors[1], windowShade); }, [colors, height, width, windowShade]); useEffect(() => { if (style === VISUALIZERS.OSCILLOSCOPE || style === VISUALIZERS.BAR) { analyser.fftSize = 2048; } }, [analyser.fftSize, style]); const paintBars = usePaintBars({ analyser, canvasCtx, renderHeight, height, windowShade, colors, }); const paintOscilloscope = usePaintOscilloscope({ analyser, canvasCtx, renderWidth, width, height, colors, }); const paintDummyFrame = usePaintDummyBarFrame({ dummyVizData, height, canvasCtx, windowShade, colors, renderHeight, }); const paintFrame = useMemo(() => { if (canvasCtx == null || status !== MEDIA_STATUS.PLAYING) { return null; } if (dummyVizData != null) { return paintDummyFrame; } switch (style) { case VISUALIZERS.OSCILLOSCOPE: return () => { canvasCtx.drawImage(bgCanvas, 0, 0); paintOscilloscope(); }; case VISUALIZERS.BAR: return () => { canvasCtx.drawImage(bgCanvas, 0, 0); paintBars(); }; } // Maybe Milkdrop is active. return null; }, [ bgCanvas, canvasCtx, dummyVizData, paintBars, paintDummyFrame, paintOscilloscope, status, style, ]); const clear = useCallback(() => { if (canvasCtx == null) { return; } canvasCtx.clearRect(0, 0, width, height); }, [canvasCtx, height, width]); useAnimationLoop({ paintFrame, clear }); return ( ); }