From d09a09ca1cb556d86d02cfdb80e93ea12a44462c Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Sat, 14 Oct 2023 19:49:39 -0700 Subject: [PATCH 1/3] [feature]: Add Equalizer Values from [this message](https://discord.com/channels/922656312888811530/922656312888811535/1162427713567596666). Works for both Electron and [modern browsers](https://caniuse.com/?search=createBiquadFilter) Notes: - it might be nice to have more filters, but I'm not touching that. Getting the Q value wrong leads to bad distortion - it would also be nice to have low/highshelf for web/mpv, but when I tried the filter was considerably stronger in web audio - better visualizer/UI? Getting the number input to behave was frustrating enough... - *please* test this. I am not confident in audio processing. --- src/renderer/app.tsx | 9 +- .../components/audio-player/index.tsx | 34 +++++- .../advanced-audio/advanced-audio-tab.tsx | 11 ++ .../advanced-audio/eqalizer-slider.tsx | 105 ++++++++++++++++++ .../components/advanced-audio/equalizer.tsx | 94 ++++++++++++++++ .../settings/components/settings-content.tsx | 14 +++ src/renderer/hooks/use-previous.ts | 10 ++ src/renderer/store/settings.store.ts | 28 +++++ src/renderer/utils/bands-to-audio-filter.ts | 10 ++ src/renderer/utils/index.ts | 1 + 10 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 src/renderer/features/settings/components/advanced-audio/advanced-audio-tab.tsx create mode 100644 src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx create mode 100644 src/renderer/features/settings/components/advanced-audio/equalizer.tsx create mode 100644 src/renderer/hooks/use-previous.ts create mode 100644 src/renderer/utils/bands-to-audio-filter.ts diff --git a/src/renderer/app.tsx b/src/renderer/app.tsx index 6d72976ab..215291cea 100644 --- a/src/renderer/app.tsx +++ b/src/renderer/app.tsx @@ -25,6 +25,7 @@ import { getMpvProperties } from '/@/renderer/features/settings/components/playb import { PlayerState, usePlayerStore, useQueueControls } from '/@/renderer/store'; import { PlaybackType, PlayerStatus } from '/@/renderer/types'; import '@ag-grid-community/styles/ag-grid.css'; +import { bandsToAudioFilter } from '/@/renderer/utils'; ModuleRegistry.registerModules([ClientSideRowModelModule, InfiniteRowModelModule]); @@ -38,6 +39,7 @@ const remote = isElectron() ? window.electron.remote : null; export const App = () => { const theme = useTheme(); const contentFont = useSettingsStore((state) => state.general.fontContent); + const audioBands = useSettingsStore((state) => state.audio.bands); const { type: playbackType } = usePlaybackSettings(); const { bindings } = useHotkeySettings(); const handlePlayQueueAdd = useHandlePlayQueueAdd(); @@ -66,12 +68,15 @@ export const App = () => { ...getMpvProperties(useSettingsStore.getState().playback.mpvProperties), }; + const volume = properties.volume; + properties.af = bandsToAudioFilter(audioBands); + mpvPlayer?.initialize({ extraParameters, properties, }); - mpvPlayer?.volume(properties.volume); + mpvPlayer?.volume(volume); } mpvPlayer?.restoreQueue(); }; @@ -85,6 +90,8 @@ export const App = () => { mpvPlayer?.stop(); mpvPlayer?.cleanup(); }; + // audioBands should NOT cause a cleanup of this function + // eslint-disable-next-line react-hooks/exhaustive-deps }, [clearQueue, playbackType]); useEffect(() => { diff --git a/src/renderer/components/audio-player/index.tsx b/src/renderer/components/audio-player/index.tsx index 541620779..6a277c9c1 100644 --- a/src/renderer/components/audio-player/index.tsx +++ b/src/renderer/components/audio-player/index.tsx @@ -7,7 +7,11 @@ import { crossfadeHandler, gaplessHandler, } from '/@/renderer/components/audio-player/utils/list-handlers'; -import { useSettingsStore } from '/@/renderer/store/settings.store'; +import { + AudioFrequencies, + useAudioSettings, + useSettingsStore, +} from '/@/renderer/store/settings.store'; import type { CrossfadeStyle } from '/@/renderer/types'; import { PlaybackStyle, PlayerStatus } from '/@/renderer/types'; @@ -35,6 +39,7 @@ const getDuration = (ref: any) => { type WebAudio = { context: AudioContext; + filters: BiquadFilterNode[]; gain: GainNode; }; @@ -54,6 +59,8 @@ export const AudioPlayer = forwardRef( }: AudioPlayerProps, ref: any, ) => { + const { bands } = useAudioSettings(); + const player1Ref = useRef(null); const player2Ref = useRef(null); const [isTransitioning, setIsTransitioning] = useState(false); @@ -122,9 +129,22 @@ export const AudioPlayer = forwardRef( sampleRate: playback.audioSampleRateHz || undefined, }); const gain = context.createGain(); - gain.connect(context.destination); + const filters: BiquadFilterNode[] = []; + + let priorNode = gain; + + for (let i = 0; i < AudioFrequencies.length; i += 1) { + const filter = context.createBiquadFilter(); + filter.type = 'peaking'; + filter.Q.value = AudioFrequencies[i].quality; + priorNode.connect(filter); + priorNode = filter; + filters.push(filter); + } - setWebAudio({ context, gain }); + priorNode.connect(context.destination); + + setWebAudio({ context, filters, gain }); return () => { return context.close(); @@ -135,6 +155,14 @@ export const AudioPlayer = forwardRef( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (webAudio) { + bands.forEach((band, idx) => { + webAudio.filters[idx].gain.value = band.gain; + }); + } + }, [bands, webAudio]); + useImperativeHandle(ref, () => ({ get player1() { return player1Ref?.current; diff --git a/src/renderer/features/settings/components/advanced-audio/advanced-audio-tab.tsx b/src/renderer/features/settings/components/advanced-audio/advanced-audio-tab.tsx new file mode 100644 index 000000000..3082b98ea --- /dev/null +++ b/src/renderer/features/settings/components/advanced-audio/advanced-audio-tab.tsx @@ -0,0 +1,11 @@ +import { Divider, Stack } from '@mantine/core'; +import { Equalizer } from '/@/renderer/features/settings/components/advanced-audio/equalizer'; + +export const AdvancedAudioTab = () => { + return ( + + + + + ); +}; diff --git a/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx b/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx new file mode 100644 index 000000000..a95c715c1 --- /dev/null +++ b/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx @@ -0,0 +1,105 @@ +import { useEffect, useState } from 'react'; +import { Group, NumberInputProps, Stack, rem } from '@mantine/core'; +import { useMove, usePrevious } from '@mantine/hooks'; +import { Text } from '/@/renderer/components/text'; +import { NumberInput } from '/@/renderer/components'; +import styled from 'styled-components'; + +interface VerticalSliderProps { + onChange: (value: number) => void; + + title?: string; + value: number; +} + +export const DB_RADIUS = 6; +const DB_DIAMATER = 2 * DB_RADIUS; +const DB_SCALE = 100 / DB_DIAMATER; + +const EqualizerNumberInput = styled(NumberInput)` + & .mantine-NumberInput-input { + text-align: center; + } +`; + +export const EqualizerSlider = ({ value, title, onChange }: VerticalSliderProps) => { + const [seekingValue, setValue] = useState(value); + + const { ref, active } = useMove(({ y }) => { + const value = Math.round((0.5 - y) * DB_DIAMATER * 10) / 10; + setValue(value); + }); + + const wasActive = usePrevious(active); + + useEffect(() => { + if (wasActive && !active) { + onChange(seekingValue); + } + }, [active, onChange, seekingValue, wasActive]); + + const displayValue = active ? seekingValue : value; + + return ( + <> + + {title && ( + + {title} + + )} +
+
+
+
+ + { + onChange(Number(val)); + }} + /> + db + + + + ); +}; diff --git a/src/renderer/features/settings/components/advanced-audio/equalizer.tsx b/src/renderer/features/settings/components/advanced-audio/equalizer.tsx new file mode 100644 index 000000000..dff5f16de --- /dev/null +++ b/src/renderer/features/settings/components/advanced-audio/equalizer.tsx @@ -0,0 +1,94 @@ +import { Grid } from '@mantine/core'; +import { EqualizerSlider } from '/@/renderer/features/settings/components/advanced-audio/eqalizer-slider'; +import { AudioFrequencies, useAudioSettings, useSettingsStoreActions } from '/@/renderer/store'; +import isElectron from 'is-electron'; +import { useCallback } from 'react'; +import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option'; +import { Button } from '/@/renderer/components'; +import { bandsToAudioFilter } from '/@/renderer/utils'; + +const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; + +export const Equalizer = () => { + const settings = useAudioSettings(); + const { setSettings } = useSettingsStoreActions(); + + const setBandSetting = useCallback( + (gain: number, index: number) => { + const bands = [...settings.bands]; + bands[index].gain = gain; + + setSettings({ + audio: { + ...settings, + bands, + }, + }); + + if (mpvPlayer) { + mpvPlayer.setProperties({ + af: bandsToAudioFilter(bands), + }); + } + }, + [setSettings, settings], + ); + + const resetBand = useCallback(() => { + const bands = AudioFrequencies.map((info) => ({ + ...info, + gain: 0, + })); + + setSettings({ + audio: { + ...settings, + bands, + }, + }); + + if (mpvPlayer) { + mpvPlayer.setProperties({ + af: bandsToAudioFilter(bands), + }); + } + }, [setSettings, settings]); + + return ( + <> + resetBand()} + > + Reset to default + + } + title="Audio Equalization" + /> + + {settings.bands.map((band, idx) => ( + + setBandSetting(gain, idx)} + /> + + ))} + + + ); +}; diff --git a/src/renderer/features/settings/components/settings-content.tsx b/src/renderer/features/settings/components/settings-content.tsx index 7d4864e08..a21b55e77 100644 --- a/src/renderer/features/settings/components/settings-content.tsx +++ b/src/renderer/features/settings/components/settings-content.tsx @@ -28,6 +28,14 @@ const HotkeysTab = lazy(() => })), ); +const AdvancedAudioTab = lazy(() => + import('/@/renderer/features/settings/components/advanced-audio/advanced-audio-tab').then( + (module) => ({ + default: module.AdvancedAudioTab, + }), + ), +); + const TabContainer = styled.div` width: 100%; height: 100%; @@ -53,6 +61,9 @@ export const SettingsContent = () => { Playback Hotkeys {isElectron() && Window} + {(isElectron() || 'AudioContext' in window) && ( + Advanced Audio settings + )} @@ -68,6 +79,9 @@ export const SettingsContent = () => { )} + + + ); diff --git a/src/renderer/hooks/use-previous.ts b/src/renderer/hooks/use-previous.ts new file mode 100644 index 000000000..2283f18ed --- /dev/null +++ b/src/renderer/hooks/use-previous.ts @@ -0,0 +1,10 @@ +import { useEffect, useRef } from 'react'; + +// from https://stackoverflow.com/questions/53446020/how-to-compare-oldvalues-and-newvalues-on-react-hooks-useeffect +export const usePrevious = (value: T): T | undefined => { + const ref = useRef(); + useEffect(() => { + ref.current = value; + }); + return ref.current; +}; diff --git a/src/renderer/store/settings.store.ts b/src/renderer/store/settings.store.ts index 60ebda84f..7b6daa930 100644 --- a/src/renderer/store/settings.store.ts +++ b/src/renderer/store/settings.store.ts @@ -110,7 +110,27 @@ export enum BindingActions { ZOOM_OUT = 'zoomOut', } +export type BandInfo = { + frequency: number; + quality: number; +}; + +export type AudioBand = { + gain: number; +} & BandInfo; + +export const AudioFrequencies: BandInfo[] = [ + { frequency: 63, quality: 0.35555555555555557 }, // 20-200 + { frequency: 400, quality: 0.6666666666666666 }, // 200-800 + { frequency: 1250, quality: 1.0416666666666667 }, // 800-2000 + { frequency: 2830, quality: 1.415 }, // 2000 - 4000 + { frequency: 5600, quality: 1.4 }, // 4000-8000 + { frequency: 12500, quality: 1.0416666666666667 }, // 8000-20000 +]; export interface SettingsState { + audio: { + bands: AudioBand[]; + }; general: { defaultFullPlaylist: boolean; followSystemTheme: boolean; @@ -208,6 +228,12 @@ const getPlatformDefaultWindowBarStyle = (): Platform => { const platformDefaultWindowBarStyle: Platform = getPlatformDefaultWindowBarStyle(); const initialState: SettingsState = { + audio: { + bands: AudioFrequencies.map((info) => ({ + ...info, + gain: 0, + })), + }, general: { defaultFullPlaylist: true, followSystemTheme: false, @@ -538,3 +564,5 @@ export const useMpvSettings = () => export const useLyricsSettings = () => useSettingsStore((state) => state.lyrics, shallow); export const useRemoteSettings = () => useSettingsStore((state) => state.remote, shallow); + +export const useAudioSettings = () => useSettingsStore((state) => state.audio, shallow); diff --git a/src/renderer/utils/bands-to-audio-filter.ts b/src/renderer/utils/bands-to-audio-filter.ts new file mode 100644 index 000000000..a81b59f89 --- /dev/null +++ b/src/renderer/utils/bands-to-audio-filter.ts @@ -0,0 +1,10 @@ +import type { AudioBand } from '/@/renderer/store'; + +export const bandsToAudioFilter = (bands: AudioBand[]): string => { + return bands + .map( + (info) => + `lavfi=[equalizer=f=${info.frequency}:width_type=o:w=${info.quality}:g=${info.gain}]`, + ) + .join(','); +}; diff --git a/src/renderer/utils/index.ts b/src/renderer/utils/index.ts index 63f206008..971bddd40 100644 --- a/src/renderer/utils/index.ts +++ b/src/renderer/utils/index.ts @@ -7,3 +7,4 @@ export * from './get-header-color'; export * from './parse-search-params'; export * from './format-duration-string'; export * from './rgb-to-rgba'; +export * from './bands-to-audio-filter'; From b0f8eb43c3d488796b98c4f7bb6b7f4e37eb92c5 Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Sun, 15 Oct 2023 21:17:35 -0700 Subject: [PATCH 2/3] bugfix: actually set frequency for peaking filters --- .../components/audio-player/index.tsx | 10 ++++---- .../components/advanced-audio/equalizer.tsx | 8 +++---- src/renderer/store/settings.store.ts | 23 +++---------------- src/renderer/utils/audio-info.ts | 10 ++++++++ src/renderer/utils/bands-to-audio-filter.ts | 7 ++---- src/renderer/utils/index.ts | 1 + 6 files changed, 24 insertions(+), 35 deletions(-) create mode 100644 src/renderer/utils/audio-info.ts diff --git a/src/renderer/components/audio-player/index.tsx b/src/renderer/components/audio-player/index.tsx index 6a277c9c1..83c663827 100644 --- a/src/renderer/components/audio-player/index.tsx +++ b/src/renderer/components/audio-player/index.tsx @@ -7,13 +7,10 @@ import { crossfadeHandler, gaplessHandler, } from '/@/renderer/components/audio-player/utils/list-handlers'; -import { - AudioFrequencies, - useAudioSettings, - useSettingsStore, -} from '/@/renderer/store/settings.store'; +import { useAudioSettings, useSettingsStore } from '/@/renderer/store/settings.store'; import type { CrossfadeStyle } from '/@/renderer/types'; import { PlaybackStyle, PlayerStatus } from '/@/renderer/types'; +import { AudioFrequencies, AudioQuality } from '/@/renderer/utils'; interface AudioPlayerProps extends ReactPlayerProps { crossfadeDuration: number; @@ -135,8 +132,9 @@ export const AudioPlayer = forwardRef( for (let i = 0; i < AudioFrequencies.length; i += 1) { const filter = context.createBiquadFilter(); + filter.frequency.value = AudioFrequencies[i]; filter.type = 'peaking'; - filter.Q.value = AudioFrequencies[i].quality; + filter.Q.value = AudioQuality; priorNode.connect(filter); priorNode = filter; filters.push(filter); diff --git a/src/renderer/features/settings/components/advanced-audio/equalizer.tsx b/src/renderer/features/settings/components/advanced-audio/equalizer.tsx index dff5f16de..58a4ad3fc 100644 --- a/src/renderer/features/settings/components/advanced-audio/equalizer.tsx +++ b/src/renderer/features/settings/components/advanced-audio/equalizer.tsx @@ -1,11 +1,11 @@ import { Grid } from '@mantine/core'; import { EqualizerSlider } from '/@/renderer/features/settings/components/advanced-audio/eqalizer-slider'; -import { AudioFrequencies, useAudioSettings, useSettingsStoreActions } from '/@/renderer/store'; +import { useAudioSettings, useSettingsStoreActions } from '/@/renderer/store'; import isElectron from 'is-electron'; import { useCallback } from 'react'; import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option'; import { Button } from '/@/renderer/components'; -import { bandsToAudioFilter } from '/@/renderer/utils'; +import { AudioFrequencies, bandsToAudioFilter } from '/@/renderer/utils'; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; @@ -35,8 +35,8 @@ export const Equalizer = () => { ); const resetBand = useCallback(() => { - const bands = AudioFrequencies.map((info) => ({ - ...info, + const bands = AudioFrequencies.map((frequency) => ({ + frequency, gain: 0, })); diff --git a/src/renderer/store/settings.store.ts b/src/renderer/store/settings.store.ts index 7b6daa930..5765391aa 100644 --- a/src/renderer/store/settings.store.ts +++ b/src/renderer/store/settings.store.ts @@ -20,7 +20,7 @@ import { TableType, Platform, } from '/@/renderer/types'; -import { randomString } from '/@/renderer/utils'; +import { AudioBand, AudioFrequencies, randomString } from '/@/renderer/utils'; const utils = isElectron() ? window.electron.utils : null; @@ -110,23 +110,6 @@ export enum BindingActions { ZOOM_OUT = 'zoomOut', } -export type BandInfo = { - frequency: number; - quality: number; -}; - -export type AudioBand = { - gain: number; -} & BandInfo; - -export const AudioFrequencies: BandInfo[] = [ - { frequency: 63, quality: 0.35555555555555557 }, // 20-200 - { frequency: 400, quality: 0.6666666666666666 }, // 200-800 - { frequency: 1250, quality: 1.0416666666666667 }, // 800-2000 - { frequency: 2830, quality: 1.415 }, // 2000 - 4000 - { frequency: 5600, quality: 1.4 }, // 4000-8000 - { frequency: 12500, quality: 1.0416666666666667 }, // 8000-20000 -]; export interface SettingsState { audio: { bands: AudioBand[]; @@ -229,8 +212,8 @@ const platformDefaultWindowBarStyle: Platform = getPlatformDefaultWindowBarStyle const initialState: SettingsState = { audio: { - bands: AudioFrequencies.map((info) => ({ - ...info, + bands: AudioFrequencies.map((frequency) => ({ + frequency, gain: 0, })), }, diff --git a/src/renderer/utils/audio-info.ts b/src/renderer/utils/audio-info.ts new file mode 100644 index 000000000..e09d43e88 --- /dev/null +++ b/src/renderer/utils/audio-info.ts @@ -0,0 +1,10 @@ +export type AudioBand = { + frequency: number; + gain: number; +}; + +// Bands from https://ia601608.us.archive.org/27/items/gov.law.ansi.s1.11.2004/ansi.s1.11.2004.pdf#page=23 +export const AudioFrequencies = [31.5, 63, 125, 250, 500, 1000, 2000, 4000, 8000, 20000]; + +// equivalent to 1/3 octave band +export const AudioQuality = 4.318473; diff --git a/src/renderer/utils/bands-to-audio-filter.ts b/src/renderer/utils/bands-to-audio-filter.ts index a81b59f89..6aa766461 100644 --- a/src/renderer/utils/bands-to-audio-filter.ts +++ b/src/renderer/utils/bands-to-audio-filter.ts @@ -1,10 +1,7 @@ -import type { AudioBand } from '/@/renderer/store'; +import { AudioBand } from '/@/renderer/utils/audio-info'; export const bandsToAudioFilter = (bands: AudioBand[]): string => { return bands - .map( - (info) => - `lavfi=[equalizer=f=${info.frequency}:width_type=o:w=${info.quality}:g=${info.gain}]`, - ) + .map((info) => `lavfi=[equalizer=f=${info.frequency}:width_type=o:w=0.3333:g=${info.gain}]`) .join(','); }; diff --git a/src/renderer/utils/index.ts b/src/renderer/utils/index.ts index 971bddd40..9f46db634 100644 --- a/src/renderer/utils/index.ts +++ b/src/renderer/utils/index.ts @@ -8,3 +8,4 @@ export * from './parse-search-params'; export * from './format-duration-string'; export * from './rgb-to-rgba'; export * from './bands-to-audio-filter'; +export * from './audio-info'; From ea6bd52bb43777d86e6a35c1a2d6f27b852ab41a Mon Sep 17 00:00:00 2001 From: Kendall Garner <17521368+kgarner7@users.noreply.github.com> Date: Thu, 23 Nov 2023 19:58:33 -0800 Subject: [PATCH 3/3] support different octave widths --- .../components/audio-player/index.tsx | 14 ++- .../advanced-audio/eqalizer-slider.tsx | 4 +- .../components/advanced-audio/equalizer.tsx | 89 ++++++++++++++----- src/renderer/store/settings.store.ts | 4 +- src/renderer/utils/audio-info.ts | 24 ++++- src/renderer/utils/bands-to-audio-filter.ts | 10 ++- 6 files changed, 112 insertions(+), 33 deletions(-) diff --git a/src/renderer/components/audio-player/index.tsx b/src/renderer/components/audio-player/index.tsx index 83c663827..09610c8bc 100644 --- a/src/renderer/components/audio-player/index.tsx +++ b/src/renderer/components/audio-player/index.tsx @@ -10,7 +10,7 @@ import { import { useAudioSettings, useSettingsStore } from '/@/renderer/store/settings.store'; import type { CrossfadeStyle } from '/@/renderer/types'; import { PlaybackStyle, PlayerStatus } from '/@/renderer/types'; -import { AudioFrequencies, AudioQuality } from '/@/renderer/utils'; +import { AudioFrequencies, octaveToQFactor } from '/@/renderer/utils'; interface AudioPlayerProps extends ReactPlayerProps { crossfadeDuration: number; @@ -56,7 +56,7 @@ export const AudioPlayer = forwardRef( }: AudioPlayerProps, ref: any, ) => { - const { bands } = useAudioSettings(); + const { bands, octave } = useAudioSettings(); const player1Ref = useRef(null); const player2Ref = useRef(null); @@ -134,7 +134,6 @@ export const AudioPlayer = forwardRef( const filter = context.createBiquadFilter(); filter.frequency.value = AudioFrequencies[i]; filter.type = 'peaking'; - filter.Q.value = AudioQuality; priorNode.connect(filter); priorNode = filter; filters.push(filter); @@ -153,6 +152,15 @@ export const AudioPlayer = forwardRef( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + if (webAudio) { + const qValue = octaveToQFactor(octave); + for (const filter of webAudio.filters) { + filter.Q.value = qValue; + } + } + }, [octave, webAudio]); + useEffect(() => { if (webAudio) { bands.forEach((band, idx) => { diff --git a/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx b/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx index a95c715c1..d03001b18 100644 --- a/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx +++ b/src/renderer/features/settings/components/advanced-audio/eqalizer-slider.tsx @@ -89,15 +89,15 @@ export const EqualizerSlider = ({ value, title, onChange }: VerticalSliderProps) min={-DB_RADIUS} precision={1} radius="xs" + rightSection="db" step={0.1} value={displayValue} variant="unstyled" - width={rem(54)} + width={rem(74)} onChange={(val) => { onChange(Number(val)); }} /> - db diff --git a/src/renderer/features/settings/components/advanced-audio/equalizer.tsx b/src/renderer/features/settings/components/advanced-audio/equalizer.tsx index 58a4ad3fc..819c79316 100644 --- a/src/renderer/features/settings/components/advanced-audio/equalizer.tsx +++ b/src/renderer/features/settings/components/advanced-audio/equalizer.tsx @@ -1,18 +1,50 @@ -import { Grid } from '@mantine/core'; +import { Grid, SelectItem } from '@mantine/core'; import { EqualizerSlider } from '/@/renderer/features/settings/components/advanced-audio/eqalizer-slider'; import { useAudioSettings, useSettingsStoreActions } from '/@/renderer/store'; import isElectron from 'is-electron'; import { useCallback } from 'react'; -import { SettingsOptions } from '/@/renderer/features/settings/components/settings-option'; -import { Button } from '/@/renderer/components'; -import { AudioFrequencies, bandsToAudioFilter } from '/@/renderer/utils'; +import { Button, Select } from '/@/renderer/components'; +import { AudioFrequencies, Octave, bandsToAudioFilter } from '/@/renderer/utils'; +import { + SettingOption, + SettingsSection, +} from '/@/renderer/features/settings/components/settings-section'; const mpvPlayer = isElectron() ? window.electron.mpvPlayer : null; +const POSSIBLE_OCTAVES: SelectItem[] = [ + { + label: 'Full octave', + value: Octave.Full, + }, + { + label: 'Third octave', + value: Octave.Third, + }, +]; + export const Equalizer = () => { const settings = useAudioSettings(); const { setSettings } = useSettingsStoreActions(); + const setOctave = useCallback( + (octave: Octave) => { + setSettings({ + audio: { + ...settings, + octave, + }, + }); + + if (mpvPlayer) { + mpvPlayer.setProperties({ + af: bandsToAudioFilter(settings.bands, octave), + }); + } + }, + [setSettings, settings], + ); + const setBandSetting = useCallback( (gain: number, index: number) => { const bands = [...settings.bands]; @@ -27,7 +59,7 @@ export const Equalizer = () => { if (mpvPlayer) { mpvPlayer.setProperties({ - af: bandsToAudioFilter(bands), + af: bandsToAudioFilter(bands, settings.octave), }); } }, @@ -49,25 +81,41 @@ export const Equalizer = () => { if (mpvPlayer) { mpvPlayer.setProperties({ - af: bandsToAudioFilter(bands), + af: bandsToAudioFilter(bands, settings.octave), }); } }, [setSettings, settings]); + const options: SettingOption[] = [ + { + control: ( +