feat(audio): graphic equalizer with presets and custom bands

Adds a 10-band graphic equalizer to AudioSettings (enabled flag +
per-band dB gains, normalised to 10 entries and clamped to range).
Presets return gain curves; the settings page gains EQ UI. libmpv
applies the filter on Linux (Android parity pending). Old persisted
settings without EQ fields load as disabled + flat.

Also includes the requirements/traceability/ux-flows doc updates for
this feature and the home long-press routing (UR-058/DR-087).

TRACES: UR-027 | IR-020, DR-030 | UT-079, UT-080, UT-081, UT-082
This commit is contained in:
2026-07-24 23:49:13 +02:00
parent 589f08b873
commit c543f90ad3
9 changed files with 1454 additions and 369 deletions
+22 -3
View File
@@ -1,12 +1,12 @@
//! Audio and video playback settings commands.
//!
//! TRACES: UR-022, UR-031, UR-032, UR-033 | DR-025, DR-034, DR-035, DR-036
//! TRACES: UR-022, UR-027, UR-031, UR-032, UR-033 | DR-025, DR-030, DR-034, DR-035, DR-036, IR-020
use tauri::State;
use super::{PlayerStateWrapper, VideoSettingsWrapper};
use crate::player::AutoplaySettings;
use crate::settings::{AudioSettings, VideoSettings};
use crate::settings::{AudioSettings, EqPreset, VideoSettings};
#[tauri::command]
#[specta::specta]
@@ -14,13 +14,32 @@ pub async fn player_set_audio_settings(
player: State<'_, PlayerStateWrapper>,
settings: AudioSettings,
) -> Result<AudioSettings, String> {
// Validate/normalise domain values before applying: clamp crossfade to its
// range and normalise the equalizer band vector (length + gain clamps).
let validated = settings
.with_crossfade_clamped()
.with_equalizer_normalised();
let mut controller = player.0.lock().await;
controller
.set_audio_settings(&settings)
.set_audio_settings(&validated)
.map_err(|e| e.to_string())?;
Ok(controller.audio_settings())
}
/// The built-in equalizer presets and their per-band gain curves (dB), for the
/// settings UI. The curve numbers are domain data defined by the band layout,
/// so the frontend reads them here rather than encoding them.
///
/// TRACES: UR-027 | DR-030
#[tauri::command]
#[specta::specta]
pub async fn player_get_eq_presets() -> Result<Vec<(EqPreset, Vec<f32>)>, String> {
Ok(EqPreset::ALL
.iter()
.map(|p| (*p, p.gains().to_vec()))
.collect())
}
#[tauri::command]
#[specta::specta]
pub async fn player_get_audio_settings(