add preserve pitch option (#972)

This commit is contained in:
Lyall 2025-06-28 21:18:08 +01:00 committed by GitHub
parent c382e01f64
commit 81ca6937bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 36 additions and 9 deletions

View file

@ -705,6 +705,8 @@
"volumeWidth_description": "the width of the volume slider", "volumeWidth_description": "the width of the volume slider",
"webAudio": "use web audio", "webAudio": "use web audio",
"webAudio_description": "use web audio. this enables advanced features like replaygain. disable if you experience otherwise", "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": "window bar style",
"windowBarStyle_description": "select the style of the window bar", "windowBarStyle_description": "select the style of the window bar",
"zoom": "zoom percentage", "zoom": "zoom percentage",

View file

@ -120,6 +120,7 @@ export const AudioPlayer = forwardRef<AudioPlayerRef, AudioPlayerProps>((props,
const audioDeviceId = useSettingsStore((state) => state.playback.audioDeviceId); const audioDeviceId = useSettingsStore((state) => state.playback.audioDeviceId);
const playback = useSettingsStore((state) => state.playback.mpvProperties); const playback = useSettingsStore((state) => state.playback.mpvProperties);
const shouldUseWebAudio = useSettingsStore((state) => state.playback.webAudio); const shouldUseWebAudio = useSettingsStore((state) => state.playback.webAudio);
const preservesPitch = useSettingsStore((state) => state.playback.preservePitch);
const { resetSampleRate } = useSettingsStoreActions(); const { resetSampleRate } = useSettingsStoreActions();
const playbackSpeed = useSpeed(); const playbackSpeed = useSpeed();
const { transcode } = usePlaybackSettings(); const { transcode } = usePlaybackSettings();
@ -230,21 +231,23 @@ export const AudioPlayer = forwardRef<AudioPlayerRef, AudioPlayerProps>((props,
// calling play() is not necessarily a safe option (https://developer.chrome.com/blog/play-request-was-interrupted) // 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: // 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. // play() + play() in rapid succession will cause problems as the frist one ends the track.
player1Ref.current const internalPlayer = player1Ref.current?.getInternalPlayer();
?.getInternalPlayer() if (internalPlayer) {
?.play() internalPlayer.preservesPitch = preservesPitch;
.catch(() => {}); internalPlayer.play().catch(() => {});
}
} else { } else {
player2Ref.current const internalPlayer = player2Ref.current?.getInternalPlayer();
?.getInternalPlayer() if (internalPlayer) {
?.play() internalPlayer.preservesPitch = preservesPitch;
.catch(() => {}); internalPlayer.play().catch(() => {});
}
} }
} else { } else {
player1Ref.current?.getInternalPlayer()?.pause(); player1Ref.current?.getInternalPlayer()?.pause();
player2Ref.current?.getInternalPlayer()?.pause(); player2Ref.current?.getInternalPlayer()?.pause();
} }
}, [currentPlayer, status]); }, [currentPlayer, status, preservesPitch]);
const handleCrossfade1 = useCallback( const handleCrossfade1 = useCallback(
(e: AudioPlayerProgress) => { (e: AudioPlayerProgress) => {

View file

@ -155,6 +155,26 @@ export const AudioSettings = ({ hasFancyAudio }: { hasFancyAudio: boolean }) =>
postProcess: 'sentenceCase', postProcess: 'sentenceCase',
}), }),
}, },
{
control: (
<Switch
defaultChecked={settings.preservePitch}
onChange={(e) => {
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: ( control: (
<Slider <Slider

View file

@ -281,6 +281,7 @@ export interface SettingsState {
mpvExtraParameters: string[]; mpvExtraParameters: string[];
mpvProperties: MpvSettings; mpvProperties: MpvSettings;
muted: boolean; muted: boolean;
preservePitch: boolean;
scrobble: { scrobble: {
enabled: boolean; enabled: boolean;
scrobbleAtDuration: number; scrobbleAtDuration: number;
@ -473,6 +474,7 @@ const initialState: SettingsState = {
replayGainPreampDB: 0, replayGainPreampDB: 0,
}, },
muted: false, muted: false,
preservePitch: true,
scrobble: { scrobble: {
enabled: true, enabled: true,
scrobbleAtDuration: 240, scrobbleAtDuration: 240,