mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-11 05:00:04 +02:00
@@ -1184,7 +1184,9 @@
|
||||
"queryBuilderCustomFields_inputLabel": "Label",
|
||||
"queryBuilderCustomFields_inputTag": "Tag",
|
||||
"queryBuilderCustomFields": "Custom fields",
|
||||
"queryBuilderCustomFields_description": "Add custom fields to use in query builders"
|
||||
"queryBuilderCustomFields_description": "Add custom fields to use in query builders",
|
||||
"microtonalPitchControls": "Microtonal pitch controls",
|
||||
"microtonalPitchControls_description": "Add more pitch controls to go between semi-tones"
|
||||
},
|
||||
"table": {
|
||||
"column": {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '/@/renderer/store';
|
||||
import {
|
||||
useCombinedLyricsAndVisualizer,
|
||||
useMicrotonalPitchControls,
|
||||
usePlaybackSettings,
|
||||
useSettingsStore,
|
||||
useSettingsStoreActions,
|
||||
@@ -21,11 +22,14 @@ import {
|
||||
useShowVisualizerInSidebar,
|
||||
} from '/@/renderer/store/settings.store';
|
||||
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
|
||||
import { Button } from '/@/shared/components/button/button';
|
||||
import { Group } from '/@/shared/components/group/group';
|
||||
import { Popover } from '/@/shared/components/popover/popover';
|
||||
import { SegmentedControl } from '/@/shared/components/segmented-control/segmented-control';
|
||||
import { Select } from '/@/shared/components/select/select';
|
||||
import { Slider } from '/@/shared/components/slider/slider';
|
||||
import { Switch } from '/@/shared/components/switch/switch';
|
||||
import { Text } from '/@/shared/components/text/text';
|
||||
import { CrossfadeStyle, PlayerStatus, PlayerStyle, PlayerType } from '/@/shared/types/types';
|
||||
|
||||
const ipc = isElectron() ? window.api.ipc : null;
|
||||
@@ -93,6 +97,11 @@ export const PlayerConfig = () => {
|
||||
id: 'playbackSpeed',
|
||||
label: t('player.playbackSpeed'),
|
||||
},
|
||||
{
|
||||
component: !preservePitch ? <PitchControls /> : <></>,
|
||||
id: 'pitchControls',
|
||||
label: '',
|
||||
},
|
||||
{
|
||||
component: (
|
||||
<Switch
|
||||
@@ -364,16 +373,15 @@ export const PlaybackSpeedSlider = () => {
|
||||
() => (value: number) => {
|
||||
const bpmValue = Number(bpm);
|
||||
if (bpmValue > 0) {
|
||||
return `${value} x / ${(bpmValue * value).toFixed(1)} BPM`;
|
||||
return `${value.toFixed(2)} x / ${(bpmValue * value).toFixed(1)} BPM`;
|
||||
}
|
||||
return `${value} x`;
|
||||
return `${value.toFixed(2)} x`;
|
||||
},
|
||||
[bpm],
|
||||
);
|
||||
|
||||
return (
|
||||
<Slider
|
||||
defaultValue={speed}
|
||||
label={formatPlaybackSpeedSliderLabel}
|
||||
marks={[
|
||||
{ label: '0.5', value: 0.5 },
|
||||
@@ -386,14 +394,81 @@ export const PlaybackSpeedSlider = () => {
|
||||
]}
|
||||
max={2}
|
||||
min={0.5}
|
||||
onChangeEnd={setSpeed}
|
||||
onChange={setSpeed}
|
||||
onDoubleClick={() => setSpeed(1)}
|
||||
step={0.01}
|
||||
styles={{
|
||||
markLabel: {},
|
||||
root: {},
|
||||
}}
|
||||
value={speed}
|
||||
w="100%"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const PitchControls = () => {
|
||||
const microtonal = useMicrotonalPitchControls();
|
||||
const speed = usePlayerSpeed();
|
||||
const { setSpeed } = usePlayerActions();
|
||||
|
||||
const speedToPitch = (speed: number) => {
|
||||
return 12 * Math.log2(speed);
|
||||
};
|
||||
|
||||
const pitchToSpeed = (pitch: number) => {
|
||||
return 2 ** (pitch / 12);
|
||||
};
|
||||
|
||||
const adjustMusicalSpeed = (adjustment: number) => {
|
||||
const curPitch = speedToPitch(speed);
|
||||
const newSpeed = pitchToSpeed(curPitch + adjustment);
|
||||
setSpeed(newSpeed);
|
||||
};
|
||||
|
||||
return (
|
||||
<Group gap={microtonal ? 'xs' : 'md'} my="sm" w="100%" wrap="nowrap">
|
||||
<Button
|
||||
aria-label="-1 semitone"
|
||||
fullWidth
|
||||
onClick={() => adjustMusicalSpeed(-1)}
|
||||
size="compact-xs"
|
||||
>
|
||||
-1st
|
||||
</Button>
|
||||
{microtonal && (
|
||||
<Button
|
||||
aria-label="-10 cents"
|
||||
fullWidth
|
||||
onClick={() => adjustMusicalSpeed(-0.1)}
|
||||
size="compact-xs"
|
||||
>
|
||||
-10ct
|
||||
</Button>
|
||||
)}
|
||||
<Text size="xs" style={{ fontFamily: 'monospace' }} ta="center" w="60px">
|
||||
{speed.toFixed(2)}% {speedToPitch(speed) > 0 && '+'}
|
||||
{speedToPitch(speed) == 0 && '±'}
|
||||
{speedToPitch(speed).toFixed(2)}st
|
||||
</Text>
|
||||
{microtonal && (
|
||||
<Button
|
||||
aria-label="+10 cents"
|
||||
fullWidth
|
||||
onClick={() => adjustMusicalSpeed(0.1)}
|
||||
size="compact-xs"
|
||||
>
|
||||
+10ct
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
aria-label="+1 semitone"
|
||||
fullWidth
|
||||
onClick={() => adjustMusicalSpeed(1)}
|
||||
size="compact-xs"
|
||||
>
|
||||
+1st
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -501,6 +501,26 @@ export const ControlSettings = memo(() => {
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
defaultChecked={settings.microtonalPitchControls}
|
||||
onChange={(e) =>
|
||||
setSettings({
|
||||
general: {
|
||||
...settings,
|
||||
microtonalPitchControls: e.currentTarget.checked,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
),
|
||||
description: t('setting.microtonalPitchControls', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: false,
|
||||
title: t('setting.microtonalPitchControls'),
|
||||
},
|
||||
];
|
||||
|
||||
return <SettingsSection options={controlOptions} title={t('page.setting.controls')} />;
|
||||
|
||||
@@ -505,6 +505,7 @@ export const GeneralSettingsSchema = z.object({
|
||||
lastFM: z.boolean(),
|
||||
lastfmApiKey: z.string(),
|
||||
listenBrainz: z.boolean(),
|
||||
microtonalPitchControls: z.boolean(),
|
||||
musicBrainz: z.boolean(),
|
||||
nativeAspectRatio: z.boolean(),
|
||||
nativeSpotify: z.boolean(),
|
||||
@@ -1201,6 +1202,7 @@ const initialState: SettingsState = {
|
||||
lastFM: true,
|
||||
lastfmApiKey: '',
|
||||
listenBrainz: true,
|
||||
microtonalPitchControls: false,
|
||||
musicBrainz: true,
|
||||
nativeAspectRatio: false,
|
||||
nativeSpotify: false,
|
||||
@@ -2830,3 +2832,6 @@ export const useButterchurnSettings = () => {
|
||||
};
|
||||
}, shallow);
|
||||
};
|
||||
|
||||
export const useMicrotonalPitchControls = () =>
|
||||
useSettingsStore((state) => state.general.microtonalPitchControls, shallow);
|
||||
|
||||
Reference in New Issue
Block a user