feat: add audio normalization toggle (MPV loudnorm filter)
This commit is contained in:
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Audioausgabe",
|
||||
"performanceOverlay": "Leistungsanzeige",
|
||||
"audioPassthrough": "Audio-Durchleitung"
|
||||
"audioPassthrough": "Audio-Durchleitung",
|
||||
"audioNormalization": "Audio-Normalisierung"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Externer Player",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Audio Output",
|
||||
"performanceOverlay": "Performance Overlay",
|
||||
"audioPassthrough": "Audio Passthrough"
|
||||
"audioPassthrough": "Audio Passthrough",
|
||||
"audioNormalization": "Audio Normalization"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "External Player",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Salida de audio",
|
||||
"performanceOverlay": "Indicador de rendimiento",
|
||||
"audioPassthrough": "Audio Passthrough"
|
||||
"audioPassthrough": "Audio Passthrough",
|
||||
"audioNormalization": "Normalización de audio"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Reproductor externo",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Sortie audio",
|
||||
"performanceOverlay": "Superposition de performance",
|
||||
"audioPassthrough": "Audio Pass-Through"
|
||||
"audioPassthrough": "Audio Pass-Through",
|
||||
"audioNormalization": "Normalisation audio"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Lecteur externe",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Uscita audio",
|
||||
"performanceOverlay": "Overlay prestazioni",
|
||||
"audioPassthrough": "Audio Passthrough"
|
||||
"audioPassthrough": "Audio Passthrough",
|
||||
"audioNormalization": "Normalizzazione audio"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Lettore esterno",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "오디오 출력",
|
||||
"performanceOverlay": "성능 오버레이",
|
||||
"audioPassthrough": "오디오 패스스루"
|
||||
"audioPassthrough": "오디오 패스스루",
|
||||
"audioNormalization": "오디오 정규화"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "외부 플레이어",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Audio-uitvoer",
|
||||
"performanceOverlay": "Prestatie-overlay",
|
||||
"audioPassthrough": "Audio-doorvoer"
|
||||
"audioPassthrough": "Audio-doorvoer",
|
||||
"audioNormalization": "Audionormalisatie"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Externe speler",
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsDe implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => 'Audioausgabe';
|
||||
@override String get performanceOverlay => 'Leistungsanzeige';
|
||||
@override String get audioPassthrough => 'Audio-Durchleitung';
|
||||
@override String get audioNormalization => 'Audio-Normalisierung';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsDe {
|
||||
'videoSettings.audioOutput' => 'Audioausgabe',
|
||||
'videoSettings.performanceOverlay' => 'Leistungsanzeige',
|
||||
'videoSettings.audioPassthrough' => 'Audio-Durchleitung',
|
||||
'videoSettings.audioNormalization' => 'Audio-Normalisierung',
|
||||
'externalPlayer.title' => 'Externer Player',
|
||||
'externalPlayer.useExternalPlayer' => 'Externen Player verwenden',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Videos in einer externen App statt im integrierten Player öffnen',
|
||||
|
||||
@@ -2276,6 +2276,9 @@ class TranslationsVideoSettingsEn {
|
||||
|
||||
/// en: 'Audio Passthrough'
|
||||
String get audioPassthrough => 'Audio Passthrough';
|
||||
|
||||
/// en: 'Audio Normalization'
|
||||
String get audioNormalization => 'Audio Normalization';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -3625,6 +3628,7 @@ extension on Translations {
|
||||
'videoSettings.audioOutput' => 'Audio Output',
|
||||
'videoSettings.performanceOverlay' => 'Performance Overlay',
|
||||
'videoSettings.audioPassthrough' => 'Audio Passthrough',
|
||||
'videoSettings.audioNormalization' => 'Audio Normalization',
|
||||
'externalPlayer.title' => 'External Player',
|
||||
'externalPlayer.useExternalPlayer' => 'Use External Player',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Open videos in an external app instead of the built-in player',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsEs implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => 'Salida de audio';
|
||||
@override String get performanceOverlay => 'Indicador de rendimiento';
|
||||
@override String get audioPassthrough => 'Audio Passthrough';
|
||||
@override String get audioNormalization => 'Normalización de audio';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsEs {
|
||||
'videoSettings.audioOutput' => 'Salida de audio',
|
||||
'videoSettings.performanceOverlay' => 'Indicador de rendimiento',
|
||||
'videoSettings.audioPassthrough' => 'Audio Passthrough',
|
||||
'videoSettings.audioNormalization' => 'Normalización de audio',
|
||||
'externalPlayer.title' => 'Reproductor externo',
|
||||
'externalPlayer.useExternalPlayer' => 'Usar reproductor externo',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Abrir vídeos en una app externa en lugar del reproductor integrado',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsFr implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => 'Sortie audio';
|
||||
@override String get performanceOverlay => 'Superposition de performance';
|
||||
@override String get audioPassthrough => 'Audio Pass-Through';
|
||||
@override String get audioNormalization => 'Normalisation audio';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsFr {
|
||||
'videoSettings.audioOutput' => 'Sortie audio',
|
||||
'videoSettings.performanceOverlay' => 'Superposition de performance',
|
||||
'videoSettings.audioPassthrough' => 'Audio Pass-Through',
|
||||
'videoSettings.audioNormalization' => 'Normalisation audio',
|
||||
'externalPlayer.title' => 'Lecteur externe',
|
||||
'externalPlayer.useExternalPlayer' => 'Utiliser un lecteur externe',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Ouvrir les vidéos dans une application externe au lieu du lecteur intégré',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsIt implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => 'Uscita audio';
|
||||
@override String get performanceOverlay => 'Overlay prestazioni';
|
||||
@override String get audioPassthrough => 'Audio Passthrough';
|
||||
@override String get audioNormalization => 'Normalizzazione audio';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsIt {
|
||||
'videoSettings.audioOutput' => 'Uscita audio',
|
||||
'videoSettings.performanceOverlay' => 'Overlay prestazioni',
|
||||
'videoSettings.audioPassthrough' => 'Audio Passthrough',
|
||||
'videoSettings.audioNormalization' => 'Normalizzazione audio',
|
||||
'externalPlayer.title' => 'Lettore esterno',
|
||||
'externalPlayer.useExternalPlayer' => 'Usa lettore esterno',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Apri i video in un\'app esterna invece del lettore integrato',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsKo implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => '오디오 출력';
|
||||
@override String get performanceOverlay => '성능 오버레이';
|
||||
@override String get audioPassthrough => '오디오 패스스루';
|
||||
@override String get audioNormalization => '오디오 정규화';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsKo {
|
||||
'videoSettings.audioOutput' => '오디오 출력',
|
||||
'videoSettings.performanceOverlay' => '성능 오버레이',
|
||||
'videoSettings.audioPassthrough' => '오디오 패스스루',
|
||||
'videoSettings.audioNormalization' => '오디오 정규화',
|
||||
'externalPlayer.title' => '외부 플레이어',
|
||||
'externalPlayer.useExternalPlayer' => '외부 플레이어 사용',
|
||||
'externalPlayer.useExternalPlayerDescription' => '내장 플레이어 대신 외부 앱에서 동영상 열기',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsNl implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => 'Audio-uitvoer';
|
||||
@override String get performanceOverlay => 'Prestatie-overlay';
|
||||
@override String get audioPassthrough => 'Audio-doorvoer';
|
||||
@override String get audioNormalization => 'Audionormalisatie';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsNl {
|
||||
'videoSettings.audioOutput' => 'Audio-uitvoer',
|
||||
'videoSettings.performanceOverlay' => 'Prestatie-overlay',
|
||||
'videoSettings.audioPassthrough' => 'Audio-doorvoer',
|
||||
'videoSettings.audioNormalization' => 'Audionormalisatie',
|
||||
'externalPlayer.title' => 'Externe speler',
|
||||
'externalPlayer.useExternalPlayer' => 'Externe speler gebruiken',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Open video\'s in een externe app in plaats van de ingebouwde speler',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsSv implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => 'Ljudutgång';
|
||||
@override String get performanceOverlay => 'Prestandaöverlägg';
|
||||
@override String get audioPassthrough => 'Ljudgenomkoppling';
|
||||
@override String get audioNormalization => 'Ljudnormalisering';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsSv {
|
||||
'videoSettings.audioOutput' => 'Ljudutgång',
|
||||
'videoSettings.performanceOverlay' => 'Prestandaöverlägg',
|
||||
'videoSettings.audioPassthrough' => 'Ljudgenomkoppling',
|
||||
'videoSettings.audioNormalization' => 'Ljudnormalisering',
|
||||
'externalPlayer.title' => 'Extern spelare',
|
||||
'externalPlayer.useExternalPlayer' => 'Använd extern spelare',
|
||||
'externalPlayer.useExternalPlayerDescription' => 'Öppna videor i en extern app istället för den inbyggda spelaren',
|
||||
|
||||
@@ -1027,6 +1027,7 @@ class _TranslationsVideoSettingsZh implements TranslationsVideoSettingsEn {
|
||||
@override String get audioOutput => '音频输出';
|
||||
@override String get performanceOverlay => '性能监控';
|
||||
@override String get audioPassthrough => '音频直通';
|
||||
@override String get audioNormalization => '音频标准化';
|
||||
}
|
||||
|
||||
// Path: externalPlayer
|
||||
@@ -2021,6 +2022,7 @@ extension on TranslationsZh {
|
||||
'videoSettings.audioOutput' => '音频输出',
|
||||
'videoSettings.performanceOverlay' => '性能监控',
|
||||
'videoSettings.audioPassthrough' => '音频直通',
|
||||
'videoSettings.audioNormalization' => '音频标准化',
|
||||
'externalPlayer.title' => '外部播放器',
|
||||
'externalPlayer.useExternalPlayer' => '使用外部播放器',
|
||||
'externalPlayer.useExternalPlayerDescription' => '在外部应用中打开视频,而不是使用内置播放器',
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "Ljudutgång",
|
||||
"performanceOverlay": "Prestandaöverlägg",
|
||||
"audioPassthrough": "Ljudgenomkoppling"
|
||||
"audioPassthrough": "Ljudgenomkoppling",
|
||||
"audioNormalization": "Ljudnormalisering"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "Extern spelare",
|
||||
|
||||
@@ -807,7 +807,8 @@
|
||||
"hdr": "HDR",
|
||||
"audioOutput": "音频输出",
|
||||
"performanceOverlay": "性能监控",
|
||||
"audioPassthrough": "音频直通"
|
||||
"audioPassthrough": "音频直通",
|
||||
"audioNormalization": "音频标准化"
|
||||
},
|
||||
"externalPlayer": {
|
||||
"title": "外部播放器",
|
||||
|
||||
@@ -79,6 +79,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
static const String _keyConfirmExitOnBack = 'confirm_exit_on_back';
|
||||
static const String _keyAmbientLighting = 'ambient_lighting';
|
||||
static const String _keyAudioPassthrough = 'audio_passthrough';
|
||||
static const String _keyAudioNormalization = 'audio_normalization';
|
||||
|
||||
SettingsService._();
|
||||
|
||||
@@ -1149,6 +1150,15 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
return prefs.getBool(_keyAudioPassthrough) ?? false;
|
||||
}
|
||||
|
||||
// Audio Normalization
|
||||
Future<void> setAudioNormalization(bool enabled) async {
|
||||
await prefs.setBool(_keyAudioNormalization, enabled);
|
||||
}
|
||||
|
||||
bool getAudioNormalization() {
|
||||
return prefs.getBool(_keyAudioNormalization) ?? false;
|
||||
}
|
||||
|
||||
// Reset all settings to defaults
|
||||
Future<void> resetAllSettings() async {
|
||||
await Future.wait([
|
||||
@@ -1206,6 +1216,7 @@ class SettingsService extends BaseSharedPreferencesService {
|
||||
prefs.remove(_keyConfirmExitOnBack),
|
||||
prefs.remove(_keyAmbientLighting),
|
||||
prefs.remove(_keyAudioPassthrough),
|
||||
prefs.remove(_keyAudioNormalization),
|
||||
prefs.remove(_keyBufferSizeMigratedToAuto),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
bool _showPerformanceOverlay = false;
|
||||
bool _autoPlayNextEpisode = true;
|
||||
bool _audioPassthrough = false;
|
||||
bool _audioNormalization = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -146,6 +147,7 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
_showPerformanceOverlay = settings.getShowPerformanceOverlay();
|
||||
_autoPlayNextEpisode = settings.getAutoPlayNextEpisode();
|
||||
_audioPassthrough = settings.getAudioPassthrough();
|
||||
_audioNormalization = settings.getAudioNormalization();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -192,6 +194,17 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
await widget.player.setAudioPassthrough(newValue);
|
||||
}
|
||||
|
||||
Future<void> _toggleAudioNormalization() async {
|
||||
final newValue = !_audioNormalization;
|
||||
final settings = await SettingsService.getInstance();
|
||||
await settings.setAudioNormalization(newValue);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_audioNormalization = newValue;
|
||||
});
|
||||
await widget.player.setProperty('af', newValue ? 'loudnorm=I=-14:TP=-3:LRA=4' : '');
|
||||
}
|
||||
|
||||
void _navigateTo(_SettingsView view) {
|
||||
// Sync views open as a compact top bar instead of a sub-view
|
||||
if (view == _SettingsView.audioSync || view == _SettingsView.subtitleSync) {
|
||||
@@ -427,6 +440,23 @@ class _VideoSettingsSheetState extends State<VideoSettingsSheet> {
|
||||
onTap: _toggleAudioPassthrough,
|
||||
),
|
||||
|
||||
// Audio Normalization (MPV only)
|
||||
if (widget.player.playerType == 'mpv')
|
||||
ListTile(
|
||||
leading: AppIcon(
|
||||
Symbols.graphic_eq_rounded,
|
||||
fill: 1,
|
||||
color: _audioNormalization ? Colors.amber : tokens(context).textMuted,
|
||||
),
|
||||
title: Text(t.videoSettings.audioNormalization),
|
||||
trailing: Switch(
|
||||
value: _audioNormalization,
|
||||
onChanged: (_) => _toggleAudioNormalization(),
|
||||
activeThumbColor: Colors.amber,
|
||||
),
|
||||
onTap: _toggleAudioNormalization,
|
||||
),
|
||||
|
||||
// Shader Preset (MPV only)
|
||||
if (widget.shaderService != null && widget.shaderService!.isSupported)
|
||||
_SettingsMenuItem(
|
||||
|
||||
Reference in New Issue
Block a user