From 81ca6937bcb2b6db2fabecaf36305e926263075e Mon Sep 17 00:00:00 2001 From: Lyall <58636255+Lyall-A@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:18:08 +0100 Subject: [PATCH] add preserve pitch option (#972) --- src/i18n/locales/en.json | 2 ++ .../components/audio-player/index.tsx | 21 +++++++++++-------- .../components/playback/audio-settings.tsx | 20 ++++++++++++++++++ src/renderer/store/settings.store.ts | 2 ++ 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index f56c5897..66325279 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -705,6 +705,8 @@ "volumeWidth_description": "the width of the volume slider", "webAudio": "use web audio", "webAudio_description": "use web audio. this enables advanced features like replaygain. disable if you experience otherwise", + "preservePitch": "preserve pitch", + "preservePitch_description": "preserves pitch when modifying playback speed", "windowBarStyle": "window bar style", "windowBarStyle_description": "select the style of the window bar", "zoom": "zoom percentage", diff --git a/src/renderer/components/audio-player/index.tsx b/src/renderer/components/audio-player/index.tsx index f5dcbc2c..7ace00f3 100644 --- a/src/renderer/components/audio-player/index.tsx +++ b/src/renderer/components/audio-player/index.tsx @@ -120,6 +120,7 @@ export const AudioPlayer = forwardRef((props, const audioDeviceId = useSettingsStore((state) => state.playback.audioDeviceId); const playback = useSettingsStore((state) => state.playback.mpvProperties); const shouldUseWebAudio = useSettingsStore((state) => state.playback.webAudio); + const preservesPitch = useSettingsStore((state) => state.playback.preservePitch); const { resetSampleRate } = useSettingsStoreActions(); const playbackSpeed = useSpeed(); const { transcode } = usePlaybackSettings(); @@ -230,21 +231,23 @@ export const AudioPlayer = forwardRef((props, // calling play() is not necessarily a safe option (https://developer.chrome.com/blog/play-request-was-interrupted) // In practice, this failure is only likely to happen when using the 0-second wav: // play() + play() in rapid succession will cause problems as the frist one ends the track. - player1Ref.current - ?.getInternalPlayer() - ?.play() - .catch(() => {}); + const internalPlayer = player1Ref.current?.getInternalPlayer(); + if (internalPlayer) { + internalPlayer.preservesPitch = preservesPitch; + internalPlayer.play().catch(() => {}); + } } else { - player2Ref.current - ?.getInternalPlayer() - ?.play() - .catch(() => {}); + const internalPlayer = player2Ref.current?.getInternalPlayer(); + if (internalPlayer) { + internalPlayer.preservesPitch = preservesPitch; + internalPlayer.play().catch(() => {}); + } } } else { player1Ref.current?.getInternalPlayer()?.pause(); player2Ref.current?.getInternalPlayer()?.pause(); } - }, [currentPlayer, status]); + }, [currentPlayer, status, preservesPitch]); const handleCrossfade1 = useCallback( (e: AudioPlayerProgress) => { diff --git a/src/renderer/features/settings/components/playback/audio-settings.tsx b/src/renderer/features/settings/components/playback/audio-settings.tsx index 03a20d19..b3fdb54c 100644 --- a/src/renderer/features/settings/components/playback/audio-settings.tsx +++ b/src/renderer/features/settings/components/playback/audio-settings.tsx @@ -155,6 +155,26 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) => postProcess: 'sentenceCase', }), }, + { + control: ( + { + setSettings({ + playback: { ...settings, preservePitch: e.currentTarget.checked }, + }); + }} + /> + ), + description: t('setting.preservePitch', { + context: 'description', + postProcess: 'sentenceCase', + }), + isHidden: settings.type !== PlaybackType.WEB, + title: t('setting.preservePitch', { + postProcess: 'sentenceCase', + }), + }, { control: (