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', () {