feat(audio): hi-res transition blend rate for crossfade, AutoDJ, and gapless (#1171)

This commit is contained in:
cucadmuh
2026-06-24 21:04:42 +03:00
committed by GitHub
parent 0b7b5df30c
commit 09f24beb32
32 changed files with 737 additions and 60 deletions
+7
View File
@@ -14,6 +14,7 @@ import { NormalizationBlock } from './audio/NormalizationBlock';
import { PlaybackRateBlock } from './audio/PlaybackRateBlock';
import { TrackTransitionsBlock } from './audio/TrackTransitionsBlock';
import { TrackPreviewsSection } from './audio/TrackPreviewsSection';
import { HiResCrossfadeResampleBlock } from './audio/HiResCrossfadeResampleBlock';
export function AudioTab() {
const { t } = useTranslation();
@@ -87,6 +88,12 @@ export function AudioTab() {
checked={auth.enableHiRes}
onChange={auth.setEnableHiRes}
/>
<HiResCrossfadeResampleBlock
enabled={auth.enableHiRes}
resampleHz={auth.hiResCrossfadeResampleHz}
onResampleHzChange={auth.setHiResCrossfadeResampleHz}
t={t}
/>
</SettingsGroup>
</div>
</SettingsSubSection>
@@ -0,0 +1,56 @@
import {
HI_RES_CROSSFADE_RESAMPLE_OPTIONS,
type HiResCrossfadeResampleHz,
sanitizeHiResCrossfadeResampleHz,
} from '../../../utils/audio/hiResCrossfadeResample';
import type { TFunction } from 'i18next';
import { SettingsGroup } from '../SettingsGroup';
interface Props {
enabled: boolean;
resampleHz: HiResCrossfadeResampleHz;
onResampleHzChange: (hz: HiResCrossfadeResampleHz) => void;
t: TFunction;
}
function labelForHz(t: TFunction, hz: HiResCrossfadeResampleHz): string {
if (hz === 88_200) return t('settings.hiResCrossfadeResample88');
if (hz === 96_000) return t('settings.hiResCrossfadeResample96');
return t('settings.hiResCrossfadeResample44');
}
/** Hi-Res crossfade / AutoDJ / gapless blend-rate picker (visible when hi-res is on). */
export function HiResCrossfadeResampleBlock({
enabled,
resampleHz,
onResampleHzChange,
t,
}: Props) {
if (!enabled) return null;
return (
<SettingsGroup>
<p className="settings-row-label" style={{ marginBottom: '0.5rem' }}>
{t('settings.hiResCrossfadeResampleTitle')}
</p>
<p className="settings-row-desc" style={{ marginBottom: '0.75rem' }}>
{t('settings.hiResCrossfadeResampleDesc')}
</p>
<div className="settings-segmented" style={{ marginBottom: '0.75rem' }}>
{HI_RES_CROSSFADE_RESAMPLE_OPTIONS.map((hz) => (
<button
key={hz}
type="button"
className={`btn ${resampleHz === hz ? 'btn-primary' : 'btn-ghost'}`}
onClick={() => onResampleHzChange(sanitizeHiResCrossfadeResampleHz(hz))}
>
{labelForHz(t, hz)}
</button>
))}
</div>
<p className="settings-row-desc" role="note" style={{ marginBottom: 0, opacity: 0.85 }}>
{t('settings.hiResCrossfadeResampleWarning')}
</p>
</SettingsGroup>
);
}
+1 -1
View File
@@ -34,7 +34,7 @@ export type SearchIndexEntry = { tab: Tab; titleKey: string; keywords?: string;
export const SETTINGS_INDEX: SearchIndexEntry[] = [
{ tab: 'audio', titleKey: 'settings.audioOutputDevice', keywords: 'output device speakers headphones alsa wasapi coreaudio' },
{ tab: 'audio', titleKey: 'settings.hiResTitle', keywords: 'hi-res hires resampling bit depth sample rate dsd 24bit' },
{ tab: 'audio', titleKey: 'settings.hiResTitle', keywords: 'hi-res hires resampling bit depth sample rate dsd 24bit crossfade autodj blend 44 88 96' },
{ tab: 'audio', titleKey: 'settings.eqTitle', keywords: 'equalizer eq bass treble autoeq filter pre-gain' },
{ tab: 'audio', titleKey: 'settings.playbackRateTitle', keywords: 'speed playback rate tempo pitch varispeed preserve corrected time stretch' },
{ tab: 'audio', titleKey: 'settings.normalization', keywords: 'normalization normalisation loudness volume leveling level replaygain replay gain lufs pre-gain' },