import { nanoid } from 'nanoid'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import styles from './visualizer-settings-form.module.css'; import i18n from '/@/i18n/i18n'; import { useSettingsStoreActions, useVisualizerSettings } from '/@/renderer/store/settings.store'; import { ActionIcon } from '/@/shared/components/action-icon/action-icon'; import { Button } from '/@/shared/components/button/button'; import { Checkbox } from '/@/shared/components/checkbox/checkbox'; import { ColorInput } from '/@/shared/components/color-input/color-input'; import { Divider } from '/@/shared/components/divider/divider'; import { Fieldset } from '/@/shared/components/fieldset/fieldset'; import { Group } from '/@/shared/components/group/group'; import { MultiSelect } from '/@/shared/components/multi-select/multi-select'; import { NumberInput } from '/@/shared/components/number-input/number-input'; import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control'; import { Select, SelectProps } from '/@/shared/components/select/select'; import { Slider, SliderProps } from '/@/shared/components/slider/slider'; import { Stack } from '/@/shared/components/stack/stack'; import { TextInput } from '/@/shared/components/text-input/text-input'; import { Text } from '/@/shared/components/text/text'; import { Textarea } from '/@/shared/components/textarea/textarea'; import { toast } from '/@/shared/components/toast/toast'; type ButterchurnPresetOption = { label: string; value: string }; let butterchurnPresetOptionsCache: ButterchurnPresetOption[] | null = null; const loadButterchurnPresetOptions = async (): Promise => { if (butterchurnPresetOptionsCache) return butterchurnPresetOptionsCache; const mod = await import('butterchurn-presets'); const presets = (mod as any).default ?? mod; const presetNames = Object.keys(presets); butterchurnPresetOptionsCache = presetNames.map((presetName) => ({ label: presetName, value: presetName, })); return butterchurnPresetOptionsCache; }; const useButterchurnPresetOptions = () => { const [options, setOptions] = useState( butterchurnPresetOptionsCache ?? [], ); useEffect(() => { if (butterchurnPresetOptionsCache) return; void loadButterchurnPresetOptions().then(setOptions); }, []); return options; }; const modeOptions: { label: string; value: string }[] = [ { label: i18n.t('visualizer.options.mode.0') as string, value: '0' }, { label: i18n.t('visualizer.options.mode.1') as string, value: '1' }, { label: i18n.t('visualizer.options.mode.2') as string, value: '2' }, { label: i18n.t('visualizer.options.mode.3') as string, value: '3' }, { label: i18n.t('visualizer.options.mode.4') as string, value: '4' }, { label: i18n.t('visualizer.options.mode.5') as string, value: '5' }, { label: i18n.t('visualizer.options.mode.6') as string, value: '6' }, { label: i18n.t('visualizer.options.mode.7') as string, value: '7' }, { label: i18n.t('visualizer.options.mode.8') as string, value: '8' }, { label: i18n.t('visualizer.options.mode.10') as string, value: '10' }, ]; const colorModeOptions: { label: string; value: string }[] = [ { label: i18n.t('visualizer.options.colorMode.gradient') as string, value: 'gradient' }, { label: i18n.t('visualizer.options.colorMode.barIndex') as string, value: 'bar-index' }, { label: i18n.t('visualizer.options.colorMode.barLevel') as string, value: 'bar-level' }, ]; const gradientOptions: { label: string; value: string }[] = [ { label: i18n.t('visualizer.options.gradient.classic') as string, value: 'classic' }, { label: i18n.t('visualizer.options.gradient.prism') as string, value: 'prism' }, { label: i18n.t('visualizer.options.gradient.rainbow') as string, value: 'rainbow' }, { label: i18n.t('visualizer.options.gradient.steelblue') as string, value: 'steelblue' }, { label: i18n.t('visualizer.options.gradient.orangered') as string, value: 'orangered' }, ]; const channelLayoutOptions: { label: string; value: string }[] = [ { label: i18n.t('visualizer.options.channelLayout.single') as string, value: 'single' }, { label: i18n.t('visualizer.options.channelLayout.dualCombined') as string, value: 'dual-combined', }, { label: i18n.t('visualizer.options.channelLayout.dualHorizontal') as string, value: 'dual-horizontal', }, { label: i18n.t('visualizer.options.channelLayout.dualVertical') as string, value: 'dual-vertical', }, ]; const fftSizeOptions: { label: string; value: string }[] = [ { label: '1024', value: '1024' }, { label: '2048', value: '2048' }, { label: '4096', value: '4096' }, { label: '8192', value: '8192' }, { label: '16384', value: '16384' }, { label: '32768', value: '32768' }, ]; const frequencyScaleOptions: { label: string; value: string }[] = [ { label: i18n.t('visualizer.options.frequencyScale.bark') as string, value: 'bark' }, { label: i18n.t('visualizer.options.frequencyScale.linear') as string, value: 'linear' }, { label: i18n.t('visualizer.options.frequencyScale.log') as string, value: 'log' }, { label: i18n.t('visualizer.options.frequencyScale.mel') as string, value: 'mel' }, ]; const weightingFilterOptions = [ { label: i18n.t('visualizer.options.weightingFilter.none') as string, value: '' }, { label: i18n.t('visualizer.options.weightingFilter.a') as string, value: 'A' }, { label: i18n.t('visualizer.options.weightingFilter.b') as string, value: 'B' }, { label: i18n.t('visualizer.options.weightingFilter.C') as string, value: 'C' }, { label: i18n.t('visualizer.options.weightingFilter.D') as string, value: 'D' }, { label: i18n.t('visualizer.options.weightingFilter.z') as string, value: 'Z' }, ]; const minFreqOptions = [ { label: '20', value: '20' }, { label: '30', value: '30' }, { label: '40', value: '40' }, { label: '50', value: '50' }, ]; const maxFreqOptions = [ { label: '8000', value: '8000' }, { label: '10000', value: '10000' }, { label: '15000', value: '15000' }, { label: '20000', value: '20000' }, { label: '22050', value: '22050' }, ]; const barSpaceOptions = [ { label: '0', value: '0' }, { label: '0.1', value: '0.1' }, { label: '0.25', value: '0.2' }, { label: '0.4', value: '0.4' }, { label: '0.5', value: '0.5' }, { label: '0.75', value: '0.7' }, { label: '1.0', value: '1.0' }, ]; const useUpdateAudioMotionAnalyzer = () => { const visualizer = useVisualizerSettings(); const { setSettings } = useSettingsStoreActions(); const updateProperty = ( property: K, value: (typeof visualizer.audiomotionanalyzer)[K], ) => { setSettings({ visualizer: { audiomotionanalyzer: { [property]: value, }, }, }); }; return { updateProperty, visualizer }; }; const useUpdateButterchurn = () => { const visualizer = useVisualizerSettings(); const { setSettings } = useSettingsStoreActions(); const updateProperty = ( property: K, value: (typeof visualizer.butterchurn)[K], ) => { setSettings({ visualizer: { butterchurn: { [property]: value, }, }, }); }; return { updateProperty, visualizer }; }; export const VisualizerSettingsForm = () => { const { t } = useTranslation(); const visualizer = useVisualizerSettings(); const { setSettings } = useSettingsStoreActions(); const visualizerTypeOptions = useMemo( () => [ { label: 'AudioMotion Analyzer', value: 'audiomotionanalyzer' }, { label: 'Butterchurn', value: 'butterchurn' }, ], [], ); const handleTypeChange = (value: string) => { setSettings({ visualizer: { type: value as 'audiomotionanalyzer' | 'butterchurn', }, }); }; return (
{visualizer.type === 'audiomotionanalyzer' && ( <> )} {visualizer.type === 'butterchurn' && ( <> )}
); }; const VisualizerSelect = (props: SelectProps) => { return