From 6d3abcd1cbfd993c3834e21a11ca9c74b901e056 Mon Sep 17 00:00:00 2001 From: Selina Li Date: Wed, 15 Mar 2023 23:33:50 -0400 Subject: [PATCH] Add clear gains and fixed band functionality --- src/common/channels.ts | 2 + src/common/constants.ts | 31 +++- src/main/main.ts | 22 +++ src/renderer/AutoEQ.tsx | 67 +++++---- src/renderer/MainContent.tsx | 169 +++++++++++++++------- src/renderer/components/FrequencyBand.tsx | 2 +- src/renderer/styles/App.scss | 2 +- src/renderer/styles/AutoEQ.scss | 6 +- src/renderer/styles/Button.scss | 1 + src/renderer/styles/MainContent.scss | 19 ++- src/renderer/utils/AquaContext.tsx | 12 +- src/renderer/utils/equalizerApi.ts | 44 +++++- 12 files changed, 277 insertions(+), 100 deletions(-) diff --git a/src/common/channels.ts b/src/common/channels.ts index 9ec27e89b..cf3cc1a6c 100644 --- a/src/common/channels.ts +++ b/src/common/channels.ts @@ -45,6 +45,8 @@ enum ChannelEnum { GET_AUTO_EQ_DEVICE_LIST = 'getAutoEqDeviceList', GET_AUTO_EQ_RESPONSE_LIST = 'getAutoEqResponseList', LOAD_AUTO_EQ_PRESET = 'loadAutoEqPreset', + CLEAR_GAINS = 'clearGains', + SET_FIXED_BAND = 'setFixedBand', } export default ChannelEnum; diff --git a/src/common/constants.ts b/src/common/constants.ts index 8d9bed2da..952524025 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -102,9 +102,28 @@ export interface IPresetV2 { /** ----- Default Values ----- */ -const FIXED_FREQUENCIES = [ - 32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000, -]; +export enum FixedBandSizeEnum { + SIX = 6, + TEN = 10, + FIFTEEN = 15, + THIRTY_ONE = 31, +} + +export const FIXED_BAND_FREQUENCIES: Record = { + [FixedBandSizeEnum.SIX]: [100, 200, 400, 800, 1600, 3200], + [FixedBandSizeEnum.TEN]: [ + 32, 64, 125, 250, 500, 1000, 2000, 4000, 8000, 16000, + ], + [FixedBandSizeEnum.FIFTEEN]: [ + 25, 40, 63, 100, 160, 250, 400, 630, 1000, 1600, 2500, 4000, 6300, 10000, + 16000, + ], + [FixedBandSizeEnum.THIRTY_ONE]: [ + 20, 25, 31.5, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, + 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, + 12500, 16000, 20000, + ], +}; const DEFAULT_FILTER_TEMPLATE = { frequency: 1000, @@ -120,9 +139,11 @@ export const getDefaultFilterWithId = (): IFilter => { }; }; -const getDefaultFilters = (): IFiltersMap => { +export const getDefaultFilters = ( + size: FixedBandSizeEnum = FixedBandSizeEnum.TEN +): IFiltersMap => { const filters: IFiltersMap = {}; - FIXED_FREQUENCIES.forEach((f) => { + FIXED_BAND_FREQUENCIES[size].forEach((f) => { const filter: IFilter = { ...getDefaultFilterWithId(), frequency: f }; filters[filter.id] = filter; }); diff --git a/src/main/main.ts b/src/main/main.ts index 12939b676..3f31cd414 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -66,6 +66,9 @@ import { WINDOW_HEIGHT_EXPANDED, WINDOW_WIDTH, getDefaultFilterWithId, + FixedBandSizeEnum, + getDefaultFilters, + IFiltersMap, } from '../common/constants'; import { ErrorCode } from '../common/errors'; import { isRestrictedPresetName } from '../common/utils'; @@ -637,6 +640,25 @@ ipcMain.on(ChannelEnum.REMOVE_FILTER, async (event, arg) => { await handleUpdate(event, channel); }); +ipcMain.on(ChannelEnum.CLEAR_GAINS, async (event) => { + const channel = ChannelEnum.CLEAR_GAINS; + + Object.keys(state.filters).forEach((key) => { + state.filters[key].gain = 0; + }); + + await handleUpdate(event, channel); +}); + +ipcMain.on(ChannelEnum.SET_FIXED_BAND, async (event, arg) => { + const channel = ChannelEnum.SET_FIXED_BAND; + const size: FixedBandSizeEnum = arg[0]; + + state.filters = getDefaultFilters(size); + + await handleUpdateHelper(event, channel, state.filters); +}); + ipcMain.on(ChannelEnum.SET_WINDOW_SIZE, async (event, arg) => { const channel = ChannelEnum.SET_WINDOW_SIZE; setWindowDimension(arg[0]); diff --git a/src/renderer/AutoEQ.tsx b/src/renderer/AutoEQ.tsx index 50e26efb8..e46aaf3bc 100644 --- a/src/renderer/AutoEQ.tsx +++ b/src/renderer/AutoEQ.tsx @@ -99,38 +99,41 @@ const AutoEQ = () => { ); return ( -
- Audio Device: - - Target Frequency Response: - setCurrentResponse(newValue)} - isDisabled={!!globalError || responses.length === 0} - emptyOptionsPlaceholder={NO_RESPONSES} - noSelectionPlaceholder={NO_RESPONSE_SELECTION} - /> - -
+ <> +

Auto EQ

+
+ Audio Device: + + Target Frequency Response: + setCurrentResponse(newValue)} + isDisabled={!!globalError || responses.length === 0} + emptyOptionsPlaceholder={NO_RESPONSES} + noSelectionPlaceholder={NO_RESPONSE_SELECTION} + /> + +
+ ); }; diff --git a/src/renderer/MainContent.tsx b/src/renderer/MainContent.tsx index e0701801a..93894386f 100644 --- a/src/renderer/MainContent.tsx +++ b/src/renderer/MainContent.tsx @@ -17,17 +17,25 @@ along with this program. If not, see . */ import { Fragment, useMemo } from 'react'; -import { MAX_NUM_FILTERS, MIN_NUM_FILTERS } from 'common/constants'; +import { + FixedBandSizeEnum, + MAX_NUM_FILTERS, + MIN_NUM_FILTERS, +} from 'common/constants'; import { computeAvgFreq } from 'common/utils'; +import { ErrorDescription } from 'common/errors'; import FrequencyBand from './components/FrequencyBand'; -import { useAquaContext } from './utils/AquaContext'; +import { FilterActionEnum, useAquaContext } from './utils/AquaContext'; import './styles/MainContent.scss'; import AddSliderDivider from './components/AddSliderDivider'; import Spinner from './icons/Spinner'; import { sortHelper } from './utils/utils'; +import Button from './widgets/Button'; +import { clearGains, setFixedBand } from './utils/equalizerApi'; const MainContent = () => { - const { filters, isLoading } = useAquaContext(); + const { filters, isLoading, dispatchFilter, setGlobalError } = + useAquaContext(); // Store widths for AddSliderDividers and FrequencyBands so we can manually position them const DIVIDER_WIDTH = 28; @@ -51,64 +59,117 @@ const MainContent = () => { return [fixedSort, visualSort, map]; }, [filters]); + const clearFilterGains = async () => { + try { + await clearGains(); + dispatchFilter({ + type: FilterActionEnum.CLEAR_GAINS, + }); + } catch (e) { + setGlobalError(e as ErrorDescription); + } + }; + + const handleFixedBand = (size: FixedBandSizeEnum) => async () => { + try { + const newFilters = await setFixedBand(size); + dispatchFilter({ + type: FilterActionEnum.INIT, + filters: newFilters, + }); + } catch (e) { + setGlobalError(e as ErrorDescription); + } + }; + return isLoading ? (
) : ( -
-
- - Filter Type - Frequency (Hz) - - +30dB - - 0dB - - -30dB - - Gain (dB) - Quality - + <> +
+

Parametric EQ

+ +
+
Fixed Band Configs
+ {Object.values(FixedBandSizeEnum) + .filter((s) => !Number.isNaN(Number(s))) + .map((size) => ( + + ))}
-
- = MAX_NUM_FILTERS} - /> - {idSortedFilters.map((filter) => { - const sliderIndex = sortIndexMap[filter.id]; - return ( - - - = MAX_NUM_FILTERS} - // Manually position the divider - style={{ - transform: `translateX(${ - (sliderIndex + 1) * (DIVIDER_WIDTH + BAND_WIDTH) - }px)`, - }} - /> - - ); - })} +
+
+ + Filter Type + Frequency (Hz) + + +30dB + + 0dB + + -30dB + + Gain (dB) + Quality + +
+
+ = MAX_NUM_FILTERS} + /> + {idSortedFilters.map((filter) => { + const sliderIndex = sortIndexMap[filter.id]; + return ( + + + = MAX_NUM_FILTERS} + // Manually position the divider + style={{ + transform: `translateX(${ + (sliderIndex + 1) * (DIVIDER_WIDTH + BAND_WIDTH) + }px)`, + }} + /> + + ); + })} +
-
+ ); }; diff --git a/src/renderer/components/FrequencyBand.tsx b/src/renderer/components/FrequencyBand.tsx index b641ee1cd..de08d8432 100644 --- a/src/renderer/components/FrequencyBand.tsx +++ b/src/renderer/components/FrequencyBand.tsx @@ -219,7 +219,7 @@ const FrequencyBand = forwardRef( const sliderHeight = useMemo( // Manually determine slider height - () => (isGraphViewOn ? '228px' : 'calc(100vh - 398px)'), + () => (isGraphViewOn ? '161px' : 'calc(100vh - 465px)'), [isGraphViewOn] ); diff --git a/src/renderer/styles/App.scss b/src/renderer/styles/App.scss index 028bf782f..52d34a90b 100644 --- a/src/renderer/styles/App.scss +++ b/src/renderer/styles/App.scss @@ -61,7 +61,7 @@ body { .middle-content { grid-area: middle-content; display: grid; - grid-template-rows: max-content 1fr; + grid-template-rows: repeat(3, max-content) 1fr; gap: $spacing-s; } diff --git a/src/renderer/styles/AutoEQ.scss b/src/renderer/styles/AutoEQ.scss index 7e72c44b0..fc30d5278 100644 --- a/src/renderer/styles/AutoEQ.scss +++ b/src/renderer/styles/AutoEQ.scss @@ -22,13 +22,17 @@ along with this program. If not, see . $auto-eq-dropdown-width: 220px; +.auto-eq-title { + margin: 0 $spacing-s; +} + .auto-eq { display: grid; grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr) max-content; height: min-content; padding: $spacing-s $spacing-m; - border-radius: $spacing-m; + border-radius: $spacing-s; background: $primary-default; align-items: center; diff --git a/src/renderer/styles/Button.scss b/src/renderer/styles/Button.scss index 3cbc8f834..c5be5ddae 100644 --- a/src/renderer/styles/Button.scss +++ b/src/renderer/styles/Button.scss @@ -38,6 +38,7 @@ $button-height: 100px; &.small { padding: $spacing-xs $spacing-s; height: fit-content; + font-size: small; } &.default { diff --git a/src/renderer/styles/MainContent.scss b/src/renderer/styles/MainContent.scss index 2fa08b538..04dc68eb2 100644 --- a/src/renderer/styles/MainContent.scss +++ b/src/renderer/styles/MainContent.scss @@ -21,6 +21,17 @@ along with this program. If not, see . @import 'spacing'; // band width and height from constants file +.main-content-title { + display: grid; + grid-template-columns: repeat(2, max-content) 1fr repeat(5, max-content); + align-items: center; + + h4, + h5, + h6 { + margin: 0 $spacing-s; + } +} .main-content { display: grid; @@ -40,7 +51,7 @@ along with this program. If not, see . display: grid; grid-template-rows: - 0px // top padding + 8px // top padding 30px // filter type 36px // frequency 24px // top arrow @@ -48,7 +59,7 @@ along with this program. If not, see . 24px // bottom arrow 36px // gain 36px // quality - 0px; // bottom padding + 16px; // bottom padding gap: 8px; } @@ -58,7 +69,9 @@ along with this program. If not, see . padding: 0 $spacing-xxs; // add some horizontal padding so the outline isn't cut off at either end height: 100%; width: 100%; - overflow-x: auto; + + // Keep scrollbar visible to prevent filters' container height from changing + overflow-x: scroll; overflow-y: hidden; } } diff --git a/src/renderer/utils/AquaContext.tsx b/src/renderer/utils/AquaContext.tsx index 55ccc5169..ea43e014d 100644 --- a/src/renderer/utils/AquaContext.tsx +++ b/src/renderer/utils/AquaContext.tsx @@ -44,6 +44,8 @@ export enum FilterActionEnum { TYPE, ADD, REMOVE, + CLEAR_GAINS, + FIXED_BAND, } type NumericalFilterAction = @@ -56,7 +58,8 @@ export type FilterAction = | { type: NumericalFilterAction; id: string; newValue: number } | { type: FilterActionEnum.TYPE; id: string; newValue: FilterTypeEnum } | { type: FilterActionEnum.ADD; id: string; frequency: number } - | { type: FilterActionEnum.REMOVE; id: string }; + | { type: FilterActionEnum.REMOVE; id: string } + | { type: FilterActionEnum.CLEAR_GAINS }; type FilterDispatch = (action: FilterAction) => void; @@ -120,6 +123,13 @@ const filterReducer: IFilterReducer = ( delete filtersCloned[action.id]; return filtersCloned; } + case FilterActionEnum.CLEAR_GAINS: { + const filtersCloned = cloneFilters(filters); + Object.values(filtersCloned).forEach((f) => { + f.gain = 0; + }); + return filtersCloned; + } default: // This throw does not actually do anything because // we are in a reducer diff --git a/src/renderer/utils/equalizerApi.ts b/src/renderer/utils/equalizerApi.ts index 034fd4a6a..f9194cbcb 100644 --- a/src/renderer/utils/equalizerApi.ts +++ b/src/renderer/utils/equalizerApi.ts @@ -24,6 +24,8 @@ import { } from 'common/errors'; import { FilterTypeEnum, + FixedBandSizeEnum, + IFiltersMap, IState, MAX_FREQUENCY, MAX_GAIN, @@ -71,7 +73,14 @@ const promisifyResult = ( }; const buildResponseHandler = < - Type extends string | number | boolean | void | IState | string[] + Type extends + | string + | number + | boolean + | void + | IState + | IFiltersMap + | string[] >( resultEvaluator: ( result: Type, @@ -94,7 +103,14 @@ const buildResponseHandler = < }; const simpleResponseHandler = < - Type extends string | number | boolean | void | IState | string[] + Type extends + | string + | number + | boolean + | void + | IState + | IFiltersMap + | string[] >() => buildResponseHandler((result, resolve) => { resolve(result); @@ -463,6 +479,30 @@ export const removeEqualizerSlider = (filterId: string): Promise => { return promisifyResult(setterResponseHandler, channel); }; +/** + * Clear gains for all filters + * @returns { Promise } exception if failed + */ +export const clearGains = (): Promise => { + const channel = ChannelEnum.CLEAR_GAINS; + window.electron.ipcRenderer.sendMessage(channel, []); + return promisifyResult(setterResponseHandler, channel); +}; + +/** + * Sets filters to be the corresponding fixed band configuration + * @param { FixedBandSizeEnum } size - Number of bands in the fixed configuration + * @returns { Promise } exception if failed + */ +export const setFixedBand = (size: FixedBandSizeEnum): Promise => { + const channel = ChannelEnum.SET_FIXED_BAND; + window.electron.ipcRenderer.sendMessage(channel, [size]); + return promisifyResult( + simpleResponseHandler(), + channel + ); +}; + /** * Increase Window Size * @returns { Promise } exception if failed.