feat(playback): global speed with three strategies (#852)

* feat(playback): global speed with three strategies

Add Settings → Audio and player-bar controls for global playback speed
(speed with auto pitch correction as default, varispeed, manual pitch shift).
Time-stretch runs on a background worker; Orbit sessions force 1.0× passthrough.

* fix(playback): align seekbar, seek, and progress on content timeline

Unify UI timebase across varispeed and preserve strategies: full-track
duration, speed-scaled progress for DSP paths, and content-timeline seeks
without varispeed scaling. Reset the sample counter after seek so clicks
land correctly; restart playback on strategy/enable changes instead of
fragile hot-switching.

* fix(ui): anchor playback speed popover like volume controls

Replace the centered EQ-style modal with a player-bar popover (outside
click, Escape, reposition on scroll). Show compact controls in the bar
and overflow menu; keep strategy hints and labels in Settings only.

* docs(release): CHANGELOG and credits for playback speed (PR #852)

* docs(changelog): add playback speed entry for PR #852

* fix(clippy): simplify raw_counter_samples branch for CI

Collapse duplicate if branches flagged by clippy::if-same-then-else.

* fix(ui): wheel on pitch slider adjusts pitch in speed popover

In compact player-bar controls, scroll over the pitch row changes pitch;
elsewhere in the panel changes speed. Stop propagation so overflow menu
wheel does not tweak volume.

* fix(playback): address PR #852 review and drop ineffective dynamic imports

Translate playback-rate strings for de/fr/es/zh/nb/nl/ro; restamp sample
counter on live preserve-path speed changes; use neutral rate atomics for
radio progress; static-import playerStore in playListenSession (move preview
volume sync to previewPlayerVolumeSync side-effect module).

* fix(i18n): translate playback-rate strategy labels in all locales

Replace leftover English Varispeed/Pitch strings in ru and other non-en
settings blocks so popover strategy buttons and hints read natively.

* fix(i18n): refine German varispeed label to "Tonhöhe folgt dem Tempo"

---------

Co-authored-by: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com>
This commit is contained in:
cucadmuh
2026-05-22 19:59:28 +03:00
committed by GitHub
parent d54eceaf3b
commit e8e41752a7
64 changed files with 2405 additions and 70 deletions
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Warteschlange einklappen, Breite ändern',
moreOptions: 'Weitere Optionen',
equalizer: 'Equalizer',
playbackRate: 'Geschwindigkeit',
miniPlayer: 'Mini-Player',
lyrics: 'Lyrics',
fsLyricsToggle: 'Lyrics im Vollbild',
+16
View File
@@ -326,8 +326,24 @@ export const settings = {
playerBarStarRating: 'Sterne-Bewertung',
playerBarFavorite: 'Favorit (Herz)',
playerBarLastfmLove: 'Last.fm Love',
playerBarPlaybackRate: 'Wiedergabegeschwindigkeit',
playerBarEqualizer: 'Equalizer',
playerBarMiniPlayer: 'Mini-Player',
playbackRateTitle: 'Wiedergabegeschwindigkeit',
playbackRateEnabled: 'Wiedergabegeschwindigkeit aktivieren',
playbackRateEnabledDesc: 'Globale Geschwindigkeit für alle Titel. Gilt nicht für Radio und Vorschau.',
playbackRateStrategy: 'Strategie',
playbackRateStrategySpeed: 'Geschwindigkeit',
playbackRateStrategyVarispeed: 'Tonhöhe folgt dem Tempo',
playbackRateStrategyPreserve: 'Tonhöhe',
playbackRateSpeed: 'Geschwindigkeit',
playbackRatePitch: 'Tonhöhe',
playbackRateDerivedPitch: 'Tonhöhenverschiebung durch Geschwindigkeit: {{value}}',
playbackRateAutoPitch: 'Die Tonhöhe wird automatisch korrigiert.',
playbackRateHint: '„Geschwindigkeit“ behält die Tonhöhe bei. „Tonhöhe folgt dem Tempo“ verschiebt die Tonhöhe mit der Geschwindigkeit. „Tonhöhe“ fügt eine manuelle Verschiebung hinzu. Höhere CPU-Last als bei „Tonhöhe folgt dem Tempo“. Nicht für Radio, Vorschau oder Orbit.',
playbackRateNeutral: 'Bei 1,0× und ohne Tonverschiebung ist die Wiedergabe normal.',
playbackRateOrbitPaused: 'Während Orbit-Sessions nicht aktiv — Wiedergabe bleibt bei 1,0× für die Synchronisation.',
playbackRateOrbitPausedShort: 'Orbit: 1,0× für Sync.',
advancedMode: 'Erweitert',
advancedModeTooltip: 'Erweiterte Optionen in allen Settings-Tabs anzeigen. Community-Beiträge, die nicht zwingend der Design-Philosophie der Maintainer von Psysonic entsprechen.',
advancedBadge: 'Erweitert',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Collapse queue, resize',
moreOptions: 'More options',
equalizer: 'Equalizer',
playbackRate: 'Playback speed',
miniPlayer: 'Mini Player',
lyrics: 'Lyrics',
fsLyricsToggle: 'Lyrics in fullscreen',
+16
View File
@@ -329,8 +329,24 @@ export const settings = {
playerBarStarRating: 'Star rating',
playerBarFavorite: 'Favorite (heart)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Playback speed',
playerBarEqualizer: 'Equalizer',
playerBarMiniPlayer: 'Mini player',
playbackRateTitle: 'Playback speed',
playbackRateEnabled: 'Enable playback speed',
playbackRateEnabledDesc: 'Change speed globally for all tracks. Not applied to radio or previews.',
playbackRateStrategy: 'Strategy',
playbackRateStrategySpeed: 'Speed',
playbackRateStrategyVarispeed: 'Varispeed',
playbackRateStrategyPreserve: 'Pitch shift',
playbackRateSpeed: 'Speed',
playbackRatePitch: 'Pitch',
playbackRateDerivedPitch: 'Pitch shift from speed: {{value}}',
playbackRateAutoPitch: 'Pitch is corrected automatically.',
playbackRateHint: 'Speed keeps pitch natural. Varispeed changes pitch with speed. Pitch shift adds a manual offset. Uses more CPU than varispeed. Not for radio, previews, or Orbit.',
playbackRateNeutral: 'At 1.0× with no pitch offset, playback is normal.',
playbackRateOrbitPaused: 'Not applied during Orbit sessions — playback stays at 1.0× for sync.',
playbackRateOrbitPausedShort: 'Orbit: 1.0× for sync.',
advancedMode: 'Advanced',
advancedModeTooltip: 'Show advanced options across all Settings tabs. Community contributions that do not necessarily reflect the design philosophy of the Psysonic maintainers.',
advancedBadge: 'Advanced',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Contraer cola, cambiar ancho',
moreOptions: 'Más opciones',
equalizer: 'Ecualizador',
playbackRate: 'Velocidad',
miniPlayer: 'Mini reproductor',
lyrics: 'Letras',
fsLyricsToggle: 'Letras en pantalla completa',
+16
View File
@@ -325,8 +325,24 @@ export const settings = {
playerBarStarRating: 'Calificación con estrellas',
playerBarFavorite: 'Favorito (corazón)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Velocidad de reproducción',
playerBarEqualizer: 'Ecualizador',
playerBarMiniPlayer: 'Mini reproductor',
playbackRateTitle: 'Velocidad de reproducción',
playbackRateEnabled: 'Activar velocidad de reproducción',
playbackRateEnabledDesc: 'Velocidad global para todas las pistas. No se aplica a la radio ni a las vistas previas.',
playbackRateStrategy: 'Estrategia',
playbackRateStrategySpeed: 'Velocidad',
playbackRateStrategyVarispeed: 'Con tono',
playbackRateStrategyPreserve: 'Tono',
playbackRateSpeed: 'Velocidad',
playbackRatePitch: 'Tono',
playbackRateDerivedPitch: 'Cambio de tono por velocidad: {{value}}',
playbackRateAutoPitch: 'El tono se corrige automáticamente.',
playbackRateHint: '« Velocidad » mantiene el tono natural. « Con tono » cambia el tono con la velocidad. « Tono » añade un desplazamiento manual. Usa más CPU que « Con tono ». No para radio, vistas previas u Orbit.',
playbackRateNeutral: 'A 1,0× y sin desplazamiento de tono, la reproducción es normal.',
playbackRateOrbitPaused: 'No se aplica durante sesiones Orbit — la reproducción permanece a 1,0× para sincronizar.',
playbackRateOrbitPausedShort: 'Orbit: 1,0× para sincronizar.',
advancedMode: 'Avanzado',
advancedModeTooltip: 'Mostrar opciones avanzadas en todas las pestañas de ajustes. Aportes de la comunidad que no reflejan necesariamente la filosofía de diseño de los mantenedores de Psysonic.',
advancedBadge: 'Avanzado',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Réduire la file, redimensionner',
moreOptions: 'Plus doptions',
equalizer: 'Égaliseur',
playbackRate: 'Vitesse',
miniPlayer: 'Mini-lecteur',
lyrics: 'Paroles',
fsLyricsToggle: 'Paroles en plein écran',
+16
View File
@@ -323,8 +323,24 @@ export const settings = {
playerBarStarRating: 'Note par étoiles',
playerBarFavorite: 'Favori (cœur)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Vitesse de lecture',
playerBarEqualizer: 'Égaliseur',
playerBarMiniPlayer: 'Mini-lecteur',
playbackRateTitle: 'Vitesse de lecture',
playbackRateEnabled: 'Activer la vitesse de lecture',
playbackRateEnabledDesc: 'Vitesse globale pour toutes les pistes. Non appliquée à la radio ni aux aperçus.',
playbackRateStrategy: 'Stratégie',
playbackRateStrategySpeed: 'Vitesse',
playbackRateStrategyVarispeed: 'Avec hauteur',
playbackRateStrategyPreserve: 'Hauteur',
playbackRateSpeed: 'Vitesse',
playbackRatePitch: 'Hauteur',
playbackRateDerivedPitch: 'Décalage de hauteur lié à la vitesse : {{value}}',
playbackRateAutoPitch: 'La hauteur est corrigée automatiquement.',
playbackRateHint: '« Vitesse » conserve la hauteur naturelle. « Avec hauteur » change la hauteur avec la vitesse. « Hauteur » ajoute un décalage manuel. Plus gourmand en CPU que « Avec hauteur ». Non pour la radio, les aperçus ou Orbit.',
playbackRateNeutral: 'À 1,0× sans décalage de hauteur, la lecture est normale.',
playbackRateOrbitPaused: 'Non appliqué pendant les sessions Orbit — la lecture reste à 1,0× pour la synchro.',
playbackRateOrbitPausedShort: 'Orbit : 1,0× pour la synchro.',
advancedMode: 'Avancé',
advancedModeTooltip: 'Afficher les options avancées dans tous les onglets des paramètres. Contributions de la communauté qui ne reflètent pas nécessairement la philosophie de conception des mainteneurs de Psysonic.',
advancedBadge: 'Avancé',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Skjul kø, endre bredde',
moreOptions: 'Flere alternativer',
equalizer: 'Equalizer',
playbackRate: 'Hastighet',
miniPlayer: 'Minispiller',
lyrics: 'Sangtekst',
fsLyricsToggle: 'Sangtekst i fullskjerm',
+16
View File
@@ -322,8 +322,24 @@ export const settings = {
playerBarStarRating: 'Stjernevurdering',
playerBarFavorite: 'Favoritt (hjerte)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Avspillingshastighet',
playerBarEqualizer: 'Equalizer',
playerBarMiniPlayer: 'Miniavspiller',
playbackRateTitle: 'Avspillingshastighet',
playbackRateEnabled: 'Aktiver avspillingshastighet',
playbackRateEnabledDesc: 'Global hastighet for alle spor. Gjelder ikke radio og forhåndsvisninger.',
playbackRateStrategy: 'Strategi',
playbackRateStrategySpeed: 'Hastighet',
playbackRateStrategyVarispeed: 'Med tone',
playbackRateStrategyPreserve: 'Tonehøyde',
playbackRateSpeed: 'Hastighet',
playbackRatePitch: 'Tonehøyde',
playbackRateDerivedPitch: 'Tonehøydeforskyvning fra hastighet: {{value}}',
playbackRateAutoPitch: 'Tonehøyden korrigeres automatisk.',
playbackRateHint: '« Hastighet » beholder naturlig tonehøyde. « Med tone » endrer tonehøyden med hastigheten. « Tonehøyde » legger til manuell forskyvning. Mer CPU enn « Med tone ». Ikke for radio, forhåndsvisninger eller Orbit.',
playbackRateNeutral: 'Ved 1,0× og uten toneforskyvning er avspilling normal.',
playbackRateOrbitPaused: 'Ikke aktiv under Orbit-økter — avspilling forblir 1,0× for synk.',
playbackRateOrbitPausedShort: 'Orbit: 1,0× for synk.',
advancedMode: 'Avansert',
advancedModeTooltip: 'Vis avanserte alternativer i alle innstillinger-faner. Bidrag fra fellesskapet som ikke nødvendigvis gjenspeiler design-filosofien til Psysonic-vedlikeholderne.',
advancedBadge: 'Avansert',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Wachtrij inklappen, breedte wijzigen',
moreOptions: 'Meer opties',
equalizer: 'Equalizer',
playbackRate: 'Snelheid',
miniPlayer: 'Minispeler',
lyrics: 'Songtekst',
fsLyricsToggle: 'Songtekst in volledig scherm',
+16
View File
@@ -323,8 +323,24 @@ export const settings = {
playerBarStarRating: 'Sterbeoordeling',
playerBarFavorite: 'Favoriet (hart)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Afspeelsnelheid',
playerBarEqualizer: 'Equalizer',
playerBarMiniPlayer: 'Mini-speler',
playbackRateTitle: 'Afspeelsnelheid',
playbackRateEnabled: 'Afspeelsnelheid inschakelen',
playbackRateEnabledDesc: 'Globale snelheid voor alle nummers. Niet van toepassing op radio en previews.',
playbackRateStrategy: 'Strategie',
playbackRateStrategySpeed: 'Snelheid',
playbackRateStrategyVarispeed: 'Met toon',
playbackRateStrategyPreserve: 'Toonhoogte',
playbackRateSpeed: 'Snelheid',
playbackRatePitch: 'Toonhoogte',
playbackRateDerivedPitch: 'Toonhoogteverschuiving door snelheid: {{value}}',
playbackRateAutoPitch: 'De toonhoogte wordt automatisch gecorrigeerd.',
playbackRateHint: '« Snelheid » behoudt de natuurlijke toonhoogte. « Met toon » verandert de toonhoogte met de snelheid. « Toonhoogte » voegt een handmatige verschuiving toe. Meer CPU dan « Met toon ». Niet voor radio, previews of Orbit.',
playbackRateNeutral: 'Bij 1,0× en zonder toonverschuiving is afspelen normaal.',
playbackRateOrbitPaused: 'Niet actief tijdens Orbit-sessies — afspelen blijft 1,0× voor sync.',
playbackRateOrbitPausedShort: 'Orbit: 1,0× voor sync.',
advancedMode: 'Geavanceerd',
advancedModeTooltip: 'Toon geavanceerde opties in alle instellingstabbladen. Bijdragen van de community die niet noodzakelijk de ontwerpfilosofie van de Psysonic-onderhouders weerspiegelen.',
advancedBadge: 'Geavanceerd',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Strânge coada, redimensionează',
moreOptions: 'Mai multe opțiuni',
equalizer: 'Egalizator',
playbackRate: 'Viteză',
miniPlayer: 'Player Mini',
lyrics: 'Versuri',
fsLyricsToggle: 'Versuri în ecran complet',
+16
View File
@@ -328,8 +328,24 @@ export const settings = {
playerBarStarRating: 'Evaluare cu stele',
playerBarFavorite: 'Favorit (inimă)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Viteză redare',
playerBarEqualizer: 'Egalizator',
playerBarMiniPlayer: 'Mini player',
playbackRateTitle: 'Viteză redare',
playbackRateEnabled: 'Activează viteza de redare',
playbackRateEnabledDesc: 'Viteză globală pentru toate piesele. Nu se aplică la radio și previzualizări.',
playbackRateStrategy: 'Strategie',
playbackRateStrategySpeed: 'Viteză',
playbackRateStrategyVarispeed: 'Cu ton',
playbackRateStrategyPreserve: 'Ton',
playbackRateSpeed: 'Viteză',
playbackRatePitch: 'Ton',
playbackRateDerivedPitch: 'Deplasare ton din viteză: {{value}}',
playbackRateAutoPitch: 'Tonul este corectat automat.',
playbackRateHint: '« Viteză » păstrează tonul natural. « Cu ton » schimbă tonul odată cu viteza. « Ton » adaugă o deplasare manuală. Consumă mai mult CPU decât « Cu ton ». Nu pentru radio, previzualizări sau Orbit.',
playbackRateNeutral: 'La 1,0× și fără deplasare de ton, redarea este normală.',
playbackRateOrbitPaused: 'Nu se aplică în sesiunile Orbit — redarea rămâne la 1,0× pentru sincronizare.',
playbackRateOrbitPausedShort: 'Orbit: 1,0× pentru sync.',
advancedMode: 'Avansat',
advancedModeTooltip: 'Afișează opțiuni avansate în toate filele de setări. Contribuții ale comunității care nu reflectă neapărat filozofia de design a mentenanților Psysonic.',
advancedBadge: 'Avansat',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: 'Свернуть очередь, изменить ширину',
moreOptions: 'Дополнительно',
equalizer: 'Эквалайзер',
playbackRate: 'Скорость',
miniPlayer: 'Мини-плеер',
lyrics: 'Текст',
lyricsLoading: 'Загрузка текста…',
+16
View File
@@ -339,8 +339,24 @@ export const settings = {
playerBarStarRating: 'Оценка звёздами',
playerBarFavorite: 'Избранное (сердечко)',
playerBarLastfmLove: 'Last.fm love',
playerBarPlaybackRate: 'Скорость',
playerBarEqualizer: 'Эквалайзер',
playerBarMiniPlayer: 'Мини-плеер',
playbackRateTitle: 'Скорость воспроизведения',
playbackRateEnabled: 'Включить изменение скорости',
playbackRateEnabledDesc: 'Глобальная скорость для всех треков. Не применяется к радио и превью.',
playbackRateStrategy: 'Стратегия',
playbackRateStrategySpeed: 'Скорость',
playbackRateStrategyVarispeed: 'С тоном',
playbackRateStrategyPreserve: 'Сдвиг тона',
playbackRateSpeed: 'Скорость',
playbackRatePitch: 'Тон',
playbackRateDerivedPitch: 'Сдвиг тона от скорости: {{value}}',
playbackRateAutoPitch: 'Тон корректируется автоматически.',
playbackRateHint: '«Скорость» сохраняет высоту тона при изменении темпа. «С тоном» меняет тон вместе со скоростью. «Сдвиг тона» — ручное смещение. Выше нагрузка на процессор, чем у режима «С тоном». Не для радио, превью и Orbit.',
playbackRateNeutral: 'При 1.0× и нулевом сдвиге тона воспроизведение обычное.',
playbackRateOrbitPaused: 'Во время сессии Orbit не применяется — воспроизведение остаётся 1.0× для синхронизации.',
playbackRateOrbitPausedShort: 'Orbit: 1.0× для синхронизации.',
advancedMode: 'Дополнительно',
advancedModeTooltip: 'Показывать расширенные параметры во всех вкладках настроек. Вклады сообщества, которые не обязательно соответствуют дизайн-философии мейнтейнеров Psysonic.',
advancedBadge: 'Дополнительно',
+1
View File
@@ -45,6 +45,7 @@ export const player = {
collapseQueueResize: '收起队列,调整宽度',
moreOptions: '更多选项',
equalizer: '均衡器',
playbackRate: '速度',
miniPlayer: '迷你播放器',
lyrics: '歌词',
fsLyricsToggle: '全屏歌词',
+16
View File
@@ -322,8 +322,24 @@ export const settings = {
playerBarStarRating: '星级评分',
playerBarFavorite: '收藏(心形)',
playerBarLastfmLove: 'Last.fm 喜欢',
playerBarPlaybackRate: '播放速度',
playerBarEqualizer: '均衡器',
playerBarMiniPlayer: '迷你播放器',
playbackRateTitle: '播放速度',
playbackRateEnabled: '启用播放速度',
playbackRateEnabledDesc: '全局调整所有曲目的播放速度。不适用于电台和预览。',
playbackRateStrategy: '策略',
playbackRateStrategySpeed: '速度',
playbackRateStrategyVarispeed: '随速变调',
playbackRateStrategyPreserve: '音高',
playbackRateSpeed: '速度',
playbackRatePitch: '音高',
playbackRateDerivedPitch: '速度引起的音高偏移:{{value}}',
playbackRateAutoPitch: '音高会自动校正。',
playbackRateHint: '「速度」保持音高不变。「随速变调」会随速度改变音高。「音高」可手动偏移。比「随速变调」占用更多 CPU。不适用于电台、预览或 Orbit。',
playbackRateNeutral: '在 1.0× 且无音高偏移时,播放为正常速度。',
playbackRateOrbitPaused: 'Orbit 会话期间不生效 — 播放保持 1.0× 以同步。',
playbackRateOrbitPausedShort: 'Orbit1.0× 同步。',
advancedMode: '高级',
advancedModeTooltip: '在所有设置选项卡中显示高级选项。由社区贡献,未必体现 Psysonic 维护者的设计理念。',
advancedBadge: '高级',