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.
This commit is contained in:
zarzet committed 2026-10-18 20:58:36 +07:00
1 parent d63d95db63
commit 68c739dfa1
4 files changed
+44 -4

No files matched your search

+9
View File
@@ -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;
+1 -1
View File
@@ -88,7 +88,7 @@ class EqualizerController extends Notifier<EqualizerState> {
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();
+5 -3
View File
@@ -483,11 +483,13 @@ class _EqualizerScreenState extends ConsumerState<EqualizerScreen> {
/// 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;
}
+29
View File
@@ -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',
() {