Add visualizer configuration (#1443)

* add visualizer configuration

* add visualizer presets

* add butterchurn visualizer

* wrap visualizers in error boundary
This commit is contained in:
Jeff
2025-12-24 18:12:13 -08:00
committed by GitHub
parent 8e04f98e26
commit d9172efae9
22 changed files with 3197 additions and 80 deletions
@@ -8,7 +8,7 @@ import styles from './full-screen-player-queue.module.css';
import { Lyrics } from '/@/renderer/features/lyrics/lyrics';
import { PlayQueue } from '/@/renderer/features/now-playing/components/play-queue';
import { FullScreenSimilarSongs } from '/@/renderer/features/player/components/full-screen-similar-songs';
import { usePlaybackSettings } from '/@/renderer/store';
import { usePlaybackSettings, useSettingsStore } from '/@/renderer/store';
import {
useFullScreenPlayerStore,
useFullScreenPlayerStoreActions,
@@ -17,8 +17,14 @@ import { Button } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group';
import { ItemListKey, PlayerType } from '/@/shared/types/types';
const Visualizer = lazy(() =>
import('/@/renderer/features/player/components/visualizer').then((module) => ({
const AudioMotionAnalyzerVisualizer = lazy(() =>
import('../../visualizer/components/audiomotionanalyzer/visualizer').then((module) => ({
default: module.Visualizer,
})),
);
const ButterchurnVisualizer = lazy(() =>
import('../../visualizer/components/butternchurn/visualizer').then((module) => ({
default: module.Visualizer,
})),
);
@@ -28,6 +34,7 @@ export const FullScreenPlayerQueue = () => {
const { activeTab, opacity } = useFullScreenPlayerStore();
const { setStore } = useFullScreenPlayerStoreActions();
const { type, webAudio } = usePlaybackSettings();
const visualizerType = useSettingsStore((store) => store.visualizer.type);
const headerItems = useMemo(() => {
const items = [
@@ -109,7 +116,11 @@ export const FullScreenPlayerQueue = () => {
<Lyrics />
) : activeTab === 'visualizer' && type === PlayerType.WEB && webAudio ? (
<Suspense fallback={<></>}>
<Visualizer />
{visualizerType === 'butterchurn' ? (
<ButterchurnVisualizer />
) : (
<AudioMotionAnalyzerVisualizer />
)}
</Suspense>
) : null}
</div>
@@ -1,11 +0,0 @@
.container {
z-index: 50;
width: 100%;
height: 100%;
margin: auto;
canvas {
width: 100%;
margin: auto;
}
}
@@ -1,40 +0,0 @@
import AudioMotionAnalyzer from 'audiomotion-analyzer';
import { createRef, useEffect, useState } from 'react';
import styles from './visualizer.module.css';
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
import { useSettingsStore } from '/@/renderer/store';
export const Visualizer = () => {
const { webAudio } = useWebAudio();
const canvasRef = createRef<HTMLDivElement>();
const accent = useSettingsStore((store) => store.general.accent);
const [motion, setMotion] = useState<AudioMotionAnalyzer>();
useEffect(() => {
const { context, gains } = webAudio || {};
if (gains && context && canvasRef.current && !motion) {
const audioMotion = new AudioMotionAnalyzer(canvasRef.current, {
ansiBands: true,
audioCtx: context,
connectSpeakers: false,
gradient: 'prism',
ledBars: true,
mode: 8,
overlay: true,
showBgColor: false,
showPeaks: false,
showScaleX: false,
showScaleY: false,
smoothing: 0.8,
});
setMotion(audioMotion);
for (const gain of gains) audioMotion.connectInput(gain);
}
return () => {};
}, [accent, canvasRef, motion, webAudio]);
return <div className={styles.container} ref={canvasRef} />;
};