From 68c739dfa1efd6f9fdb2e8b63f78a546e55e3e9a Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Sun, 18 Oct 2026 20:58:36 +0700 Subject: [PATCH] fix(equalizer): keep headroom and limiter choices when applying a preset Applying a preset replaced every setting, so built-in presets silently turned automatic headroom, the limiter and the compressor back on and reset balance. Presets now carry only the curve, preamp, bass and treble. The active preset is matched on the same fields, so it no longer shows as Custom once headroom is off. --- lib/models/equalizer_settings.dart | 9 +++++++++ lib/providers/equalizer_provider.dart | 2 +- lib/screens/equalizer_screen.dart | 8 +++++--- test/equalizer_test.dart | 29 +++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/models/equalizer_settings.dart b/lib/models/equalizer_settings.dart index 3f93914e..14a3965c 100644 --- a/lib/models/equalizer_settings.dart +++ b/lib/models/equalizer_settings.dart @@ -130,6 +130,15 @@ class EqualizerSettings { return copyWith(gains: [for (final hz in next.frequencies) gainAt(hz)]); } + /// A preset carries a sound; headroom, limiting, compression and balance + /// stay as the listener set them. + EqualizerSettings withToneOf(EqualizerSettings preset) => copyWith( + gains: preset.gains, + preamp: preset.preamp, + bass: preset.bass, + treble: preset.treble, + ); + double gainAt(double hz, {bool includeTone = false}) { final values = includeTone ? effectiveGains : gains; final centers = frequencies; diff --git a/lib/providers/equalizer_provider.dart b/lib/providers/equalizer_provider.dart index 4e25a355..fb6cee24 100644 --- a/lib/providers/equalizer_provider.dart +++ b/lib/providers/equalizer_provider.dart @@ -88,7 +88,7 @@ class EqualizerController extends Notifier { void update(EqualizerSettings settings) => _commit(settings, state.presets); void applyPreset(EqualizerPreset preset) => - update(preset.settings.copyWith(enabled: state.settings.enabled)); + update(state.settings.withToneOf(preset.settings)); void savePreset(String name) { final trimmed = name.trim(); diff --git a/lib/screens/equalizer_screen.dart b/lib/screens/equalizer_screen.dart index 6bdf1fa2..5a856a2f 100644 --- a/lib/screens/equalizer_screen.dart +++ b/lib/screens/equalizer_screen.dart @@ -483,11 +483,13 @@ class _EqualizerScreenState extends ConsumerState { /// The preset whose curve, tone and dynamics match the current settings. EqualizerPreset? _matchingPreset(EqualizerState state) { - String key(EqualizerSettings settings) => - jsonEncode(settings.copyWith(enabled: false).toJson()); + // Matches when applying the preset would change nothing. + String key(EqualizerSettings settings) => jsonEncode(settings.toJson()); final current = key(state.settings); for (final preset in [...EqualizerPreset.builtIn, ...state.presets]) { - if (key(preset.settings) == current) return preset; + if (key(state.settings.withToneOf(preset.settings)) == current) { + return preset; + } } return null; } diff --git a/test/equalizer_test.dart b/test/equalizer_test.dart index 923b5cd3..86904d8a 100644 --- a/test/equalizer_test.dart +++ b/test/equalizer_test.dart @@ -60,6 +60,35 @@ void main() { expect(settings.copyWith(autoHeadroom: false).inputGain, -2); }); + test('presets change the sound but keep output safety choices', () { + final container = ProviderContainer(); + addTearDown(container.dispose); + final notifier = container.read(equalizerProvider.notifier); + notifier.update( + const EqualizerSettings( + enabled: true, + balance: 0.25, + autoHeadroom: false, + limiter: false, + compressor: true, + ratio: 6, + ), + ); + final bassBoost = EqualizerPreset.builtIn.firstWhere( + (preset) => preset.name == 'Bass boost', + ); + notifier.applyPreset(bassBoost); + final settings = container.read(equalizerProvider).settings; + expect(settings.gains, bassBoost.settings.gains); + expect(settings.enabled, isTrue); + expect(settings.balance, 0.25); + expect(settings.autoHeadroom, isFalse); + expect(settings.limiter, isFalse); + expect(settings.compressor, isTrue); + expect(settings.ratio, 6); + expect(settings.inputGain, 0); + }); + test( 'malformed and nonfinite imports cannot create unsafe native settings', () {