diff --git a/src/components/WaveformSeek.tsx b/src/components/WaveformSeek.tsx index 274b5b62..e3d9636a 100644 --- a/src/components/WaveformSeek.tsx +++ b/src/components/WaveformSeek.tsx @@ -750,7 +750,8 @@ export function drawSeekbar( ) { const anim = animState ?? makeAnimState(); switch (style) { - case 'waveform': drawWaveform(canvas, heights, progress, buffered); break; + case 'truewave': drawWaveform(canvas, heights, progress, buffered); break; + case 'pseudowave': drawWaveform(canvas, heights, progress, buffered); break; case 'linedot': drawLineDot(canvas, progress, buffered); break; case 'bar': drawBar(canvas, progress, buffered); break; case 'thick': drawThick(canvas, progress, buffered); break; @@ -782,7 +783,7 @@ export function SeekbarPreview({ const canvas = canvasRef.current; if (!canvas) return; let heights: Float32Array | null = null; - if (style === 'waveform') { + if (style === 'truewave' || style === 'pseudowave') { heights = makeHeights('seekbar-preview-demo'); } const animState = makeAnimState(); @@ -896,6 +897,17 @@ export default function WaveformSeek({ trackId }: Props) { heightsRef.current = null; return; } + // Pseudowave is the deterministic per-track-ID variant — no analysis needed, + // no morph animation, no flat-fallback. It just sits there looking like a + // waveform. + if (seekbarStyle === 'pseudowave') { + heightsRef.current = makeHeights(trackId); + const canvas = canvasRef.current; + if (canvas && !ANIMATED_STYLES.has(seekbarStyle)) { + drawSeekbar(canvas, seekbarStyle, heightsRef.current, progressRef.current, bufferedRef.current, animStateRef.current); + } + return; + } if (waveformBins && waveformBins.length > 0) { const h = binsToHeights(waveformBins); const prev = heightsRef.current; @@ -959,7 +971,7 @@ export default function WaveformSeek({ trackId }: Props) { } // No analysis bins yet: render 500 flat bars immediately. heightsRef.current = makeFlatWaveHeights(); - }, [trackId, waveformBins]); + }, [trackId, waveformBins, seekbarStyle]); // Imperative subscription — no React re-renders from progress changes. // Static styles draw here; animated styles only update refs. diff --git a/src/locales/de.ts b/src/locales/de.ts index ee5d6c94..ec64c99e 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -888,7 +888,8 @@ export const deTranslation = { sidebarLyricsStyleAppleDesc: 'Aktive Zeile oben mit sanfter Animation.', seekbarStyle: 'Seekbar-Stil', seekbarStyleDesc: 'Aussehen der Wiedergabe-Seekbar auswählen', - seekbarWaveform: 'Wellenform', + seekbarTruewave: 'Echte Wellenform', + seekbarPseudowave: 'Pseudo-Wellenform', seekbarLinedot: 'Linie & Punkt', seekbarBar: 'Balken', seekbarThick: 'Dicker Balken', diff --git a/src/locales/en.ts b/src/locales/en.ts index ae79cfe7..1ab2e6ad 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -894,7 +894,8 @@ export const enTranslation = { fsPortraitDim: 'Photo dimming', seekbarStyle: 'Seekbar Style', seekbarStyleDesc: 'Choose the look of the player seek bar', - seekbarWaveform: 'Waveform', + seekbarTruewave: 'Truewave', + seekbarPseudowave: 'Pseudowave', seekbarLinedot: 'Line & Dot', seekbarBar: 'Bar', seekbarThick: 'Thick Bar', diff --git a/src/locales/es.ts b/src/locales/es.ts index 848ac685..0b4ed21f 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -881,7 +881,8 @@ export const esTranslation = { sidebarLyricsStyleAppleDesc: 'La línea activa se ancla arriba con animación suave.', seekbarStyle: 'Estilo de Barra de Progreso', seekbarStyleDesc: 'Elige la apariencia de la barra de progreso del reproductor', - seekbarWaveform: 'Forma de Onda', + seekbarTruewave: 'Forma de Onda Real', + seekbarPseudowave: 'Forma de Onda Pseudo', seekbarLinedot: 'Línea y Punto', seekbarBar: 'Barra', seekbarThick: 'Barra Gruesa', diff --git a/src/locales/fr.ts b/src/locales/fr.ts index 96b25972..58800d30 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -876,7 +876,8 @@ export const frTranslation = { sidebarLyricsStyleAppleDesc: "La ligne active reste en haut avec une animation fluide.", seekbarStyle: 'Style de la barre de lecture', seekbarStyleDesc: 'Choisir l\'apparence de la barre de progression', - seekbarWaveform: 'Forme d\'onde', + seekbarTruewave: 'Forme d\'onde réelle', + seekbarPseudowave: 'Forme d\'onde pseudo', seekbarLinedot: 'Ligne & point', seekbarBar: 'Barre', seekbarThick: 'Barre épaisse', diff --git a/src/locales/nb.ts b/src/locales/nb.ts index 6f0dc8c5..1cf9c7d0 100644 --- a/src/locales/nb.ts +++ b/src/locales/nb.ts @@ -875,7 +875,8 @@ export const nbTranslation = { sidebarLyricsStyleAppleDesc: 'Aktiv linje forankres øverst med jevn animasjon.', seekbarStyle: 'Søkefelt-stil', seekbarStyleDesc: 'Velg utseendet på avspillingssøkefeltet', - seekbarWaveform: 'Bølgeform', + seekbarTruewave: 'Ekte bølgeform', + seekbarPseudowave: 'Pseudo-bølgeform', seekbarLinedot: 'Linje & punkt', seekbarBar: 'Linje', seekbarThick: 'Tykk linje', diff --git a/src/locales/nl.ts b/src/locales/nl.ts index 22796f55..f8a6ff51 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -875,7 +875,8 @@ export const nlTranslation = { sidebarLyricsStyleAppleDesc: 'Actieve regel verankerd bovenaan met vloeiende animatie.', seekbarStyle: 'Zoekbalkstijl', seekbarStyleDesc: 'Kies het uiterlijk van de afspeelbalk', - seekbarWaveform: 'Golfvorm', + seekbarTruewave: 'Echte golfvorm', + seekbarPseudowave: 'Pseudo-golfvorm', seekbarLinedot: 'Lijn & punt', seekbarBar: 'Balk', seekbarThick: 'Dikke balk', diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 24c4883c..ba7c2187 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -927,7 +927,8 @@ export const ruTranslation = { sidebarLyricsStyleAppleDesc: 'Активная строка фиксируется сверху с плавной анимацией.', seekbarStyle: 'Стиль прогресс-бара', seekbarStyleDesc: 'Выбор внешнего вида полосы воспроизведения', - seekbarWaveform: 'Форма волны', + seekbarTruewave: 'Реальная форма волны', + seekbarPseudowave: 'Псевдо форма волны', seekbarLinedot: 'Линия и точка', seekbarBar: 'Полоса', seekbarThick: 'Толстая полоса', diff --git a/src/locales/zh.ts b/src/locales/zh.ts index 3da16846..f9feee91 100644 --- a/src/locales/zh.ts +++ b/src/locales/zh.ts @@ -870,7 +870,8 @@ export const zhTranslation = { sidebarLyricsStyleAppleDesc: '活动行固定在顶部,带有平滑弹簧动画。', seekbarStyle: '进度条样式', seekbarStyleDesc: '选择播放进度条的外观', - seekbarWaveform: '波形', + seekbarTruewave: '真实波形', + seekbarPseudowave: '伪波形', seekbarLinedot: '线条与点', seekbarBar: '条形', seekbarThick: '粗条形', diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 3d7f5f51..6b5b56e7 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -3676,7 +3676,7 @@ export default function Settings() { {t('settings.seekbarStyleDesc')}
- {(['waveform', 'linedot', 'bar', 'thick', 'segmented', 'neon', 'pulsewave', 'particletrail', 'liquidfill', 'retrotape'] as SeekbarStyle[]).map(style => ( + {(['truewave', 'pseudowave', 'linedot', 'bar', 'thick', 'segmented', 'neon', 'pulsewave', 'particletrail', 'liquidfill', 'retrotape'] as SeekbarStyle[]).map(style => ( ()( fsPortraitDim: 28, showChangelogOnUpdate: true, lastSeenChangelogVersion: '', - seekbarStyle: 'waveform', + seekbarStyle: 'truewave', enableHiRes: false, audioOutputDevice: null, hotCacheEnabled: false, @@ -729,6 +729,13 @@ export const useAuthStore = create()( } catch { /* ignore */ } } + // One-time: 'waveform' style was renamed to 'truewave' (with 'pseudowave' + // added as the deterministic legacy variant). Existing persisted value + // 'waveform' should land on the new bins-based default. + const seekbarStyleMigrated = (state.seekbarStyle as string) === 'waveform' + ? { seekbarStyle: 'truewave' as SeekbarStyle } + : {}; + const st = state as { loudnessTargetLufs?: unknown; loudnessPreAnalysisAttenuationDb?: unknown }; const targetSan = sanitizeLoudnessLufsPreset(st.loudnessTargetLufs, -12); const preSan = clampLoudnessPreAnalysisAttenuationDb(st.loudnessPreAnalysisAttenuationDb); @@ -745,6 +752,7 @@ export const useAuthStore = create()( ...conflictingLegacyState, ...lyricsSourcesMigrated, ...wheelSmoothOneTime, + ...seekbarStyleMigrated, }); }, }