mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
feat(audio): bit-perfect hi-res playback + underrun hardening (opt-in alpha)
Safe mode (default): all audio outputs at 44.1 kHz, rodio resamples internally. Maximum stability out of the box. Hi-Res mode (alpha toggle): stream opens at the file's native sample rate (e.g. 88.2/96/192 kHz) for bit-perfect output. Rust (audio.rs): - open_stream_for_rate(): CPAL queries device supported configs, finds exact rate match or falls back to highest available / device default. - create_engine() thread: ThreadPriority::Max (silently ignored without CAP_SYS_NICE), loop handles rate-switch requests, PIPEWIRE_LATENCY scales with rate (8192 frames for >48 kHz ≈ 93 ms quantum), keeps PULSE_LATENCY_MSEC in sync. - audio_play(): hi_res_enabled param gates effective_rate (44100 vs native); stream re-opens only when needed; 150 ms PipeWire settle + 500 ms sink pre-fill (pause→append→sleep→play) for hi-res tracks. - audio_chain_preload(): hi_res_enabled param; rate-mismatch bail skipped in safe mode so gapless chains always work at 44.1 kHz. - SizedDecoder MSS buffer: 4 MB (was 512 KB) to reduce Symphonia read overhead on high-bitrate hi-res files. - thread-priority crate added to Cargo.toml. Frontend: - authStore: enableHiRes (bool, default false) + setEnableHiRes. - playerStore: hiResEnabled passed to all audio_play / audio_chain_preload invoke calls. - Settings → Audio tab: "Native Hi-Res Playback" toggle with Alpha badge and stability warning, i18n for all 7 languages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+36
-1
@@ -5,7 +5,7 @@ import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import {
|
||||
Wifi, WifiOff, Globe, Music2, Sliders, LogOut, CheckCircle2, FolderOpen,
|
||||
Palette, Server, Plus, Trash2, Eye, EyeOff, Info, ExternalLink, Shuffle, X, Play, Type, Keyboard, ChevronDown,
|
||||
GripVertical, PanelLeft, RotateCcw, LayoutGrid, AppWindow, HardDrive, Upload, Download
|
||||
GripVertical, PanelLeft, RotateCcw, LayoutGrid, AppWindow, HardDrive, Upload, Download, Waves
|
||||
} from 'lucide-react';
|
||||
import { exportBackup, importBackup } from '../utils/backup';
|
||||
import { showToast } from '../utils/toast';
|
||||
@@ -545,6 +545,41 @@ export default function Settings() {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Native Hi-Res Playback */}
|
||||
<section className="settings-section">
|
||||
<div className="settings-section-header">
|
||||
<Waves size={18} />
|
||||
<h2 style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
{t('settings.hiResTitle')}
|
||||
<span style={{
|
||||
fontSize: 10, fontWeight: 600, textTransform: 'uppercase',
|
||||
letterSpacing: '0.04em', padding: '2px 6px', borderRadius: 4,
|
||||
background: 'color-mix(in srgb, var(--color-warning, #f59e0b) 22%, transparent)',
|
||||
color: 'var(--text-primary)',
|
||||
}}>
|
||||
{t('settings.hotCacheAlphaBadge')}
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div className="settings-card">
|
||||
<div className="settings-toggle-row">
|
||||
<div>
|
||||
<div style={{ fontWeight: 500 }}>{t('settings.hiResEnabled')}</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)' }}>{t('settings.hiResDesc')}</div>
|
||||
</div>
|
||||
<label className="toggle-switch" aria-label={t('settings.hiResEnabled')}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={auth.enableHiRes}
|
||||
onChange={e => auth.setEnableHiRes(e.target.checked)}
|
||||
id="hires-enabled-toggle"
|
||||
/>
|
||||
<span className="toggle-track" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user