feat(ui): MAIN and ADVANCED pages, Anvil main page around Mode, Split and Sub level

Producers found the panels crowded: every control had the same weight. The
header now carries a MAIN / ADVANCED switch (common-ui header_paged). The
main page keeps the controls a first result needs; the advanced page shows
everything, as before.

- Anvil: Mode (Replace or Enhance), Split and Sub level, plus Octave, Ghost,
  Keep and Output. Kick lock, Dynamics, Drive, Glide, Phase, Gate and the
  pitch switches move to Advanced. The note readout says when the original
  sub plays because tracking is unsure.
- Lathe, Temper, Knurl, Planish, Braze: tracking and fine-tuning controls
  (Gate, Smooth, Snap, Harmonics, Push at, Floor, Bright, Normalize, band
  gains, Phase) move to Advanced. Lathe labels the octaves of the note in Hz
  on the harmonic axis.
- Forge passes the page to its module pages.
- Weld: the main page shows the level won back at full height, Mode,
  Response and Output; the phase plot, Sections, Low, High and Gate move to
  Advanced.
- editor-check renders the advanced pages with --advanced.
This commit is contained in:
nvrl committed 2026-11-06 21:16:11 +01:00
1 parent d6939a753a
commit 01fd3241eb
10 files changed
+594 -227

No files matched your search

+52 -21
View File
@@ -14,6 +14,8 @@ pub struct State {
pub help: String,
/// Last point of a draw stroke in the harmonic editor: (harmonic index, dB).
stroke: Option<(usize, f32)>,
/// The advanced page shows its extra controls. Not saved.
pub advanced: bool,
}
impl Default for State {
fn default() -> Self {
@@ -22,6 +24,7 @@ impl Default for State {
gesture_active: false,
help: DEFAULT_HELP.into(),
stroke: None,
advanced: false,
}
}
}
@@ -77,6 +80,13 @@ fn y_gain(rect: Rect, y: f32) -> f32 {
}
/// Bar editor of the per-harmonic gains, with the effective curve and measured levels.
fn hz_label(hz: f32) -> String {
if hz >= 1000. {
format!("{:.1}k", hz / 1000.)
} else {
format!("{hz:.0}")
}
}
fn harmonic_editor(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State, height: f32) {
let size = Vec2::new(ui.available_width(), height);
let (outer, response) = ui.allocate_exact_size(size, Sense::click_and_drag());
@@ -199,6 +209,17 @@ fn harmonic_editor(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State
FontId::proportional(10.),
label_color,
);
// Where the harmonic sits now, at the octaves of the fundamental.
let hz = state.analyzer.hz;
if hz > 0. && (k + 1).is_power_of_two() {
p.text(
Pos2::new(x0 + col * 0.5, gains.bottom() - 2.),
egui::Align2::CENTER_BOTTOM,
hz_label(hz * (k + 1) as f32),
FontId::proportional(9.),
DIM,
);
}
// Measured levels: input (steel) and output (accent), side by side.
let f = &state.analyzer;
for (i, (db, color)) in [(f.input[k], STEEL), (f.output[k], ACCENT)]
@@ -269,7 +290,8 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
state.help = DEFAULT_HELP.into();
state.gesture_active = ui.input(|i| i.pointer.any_down());
ui.add_space(2.);
header(ui, "LATHE", "HARMONIC EQ", |ui| {
let mut advanced = state.advanced;
header_paged(ui, "LATHE", "HARMONIC EQ", Some(&mut advanced), |ui| {
let r = ui
.selectable_label(settings.bypass, RichText::new("BYPASS").size(13.))
.on_hover_text("Crossfade to the delayed dry signal.");
@@ -281,6 +303,7 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
pitch_from(ui, &mut mode, &mut settings.pitch);
settings.source = Source::from_mode(mode);
});
state.advanced = advanced;
body(ui, settings, state);
ui.separator();
ui.label(RichText::new(&state.help).color(MUTED));
@@ -391,21 +414,24 @@ fn note_panel(
hint(&r, state, help);
}
});
ui.add_space(10.);
let r = ui.selectable_label(settings.snap, RichText::new("SNAP TO SEMITONE").size(13.));
if r.clicked() {
settings.snap = !settings.snap;
if state.advanced {
ui.add_space(10.);
let r = ui.selectable_label(settings.snap, RichText::new("SNAP TO SEMITONE").size(13.));
if r.clicked() {
settings.snap = !settings.snap;
}
hint(
&r,
state,
"Round the detected pitch. Turn off for slides and pitch bends.",
);
}
hint(
&r,
state,
"Round the detected pitch. Turn off for slides and pitch bends.",
);
}
fn controls(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
let d = Settings::default();
ui.columns(9, |c| {
let advanced = state.advanced;
ui.columns(if advanced { 9 } else { 6 }, |c| {
knob(&mut c[0], state, "Tilt", &mut settings.tilt, -12. ..=12., d.tilt, " dB/oct", 80.,
"Brighter or darker by harmonic number: dB per octave of harmonics, pivot on the fundamental.");
knob(&mut c[1], state, "Odd/Even", &mut settings.odd_even, -12. ..=12., d.odd_even, " dB", 80.,
@@ -414,17 +440,22 @@ fn controls(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
"Gain between the harmonics. Cut to clean noise and beating, lift for grit.");
knob(&mut c[3], state, "Width", &mut settings.width, 5. ..=50., d.width, " %", 80.,
"Bandwidth of every band as a share of the harmonic spacing.");
let mut harmonics = settings.harmonics as f32;
knob(&mut c[4], state, "Harmonics", &mut harmonics, 8. ..=32., d.harmonics as f32, "", 54.,
"How many harmonics are shaped. Higher harmonics pass unchanged.");
settings.harmonics = harmonics.round() as u32;
knob(&mut c[5], state, "Smooth", &mut settings.smooth, 0. ..=100., d.smooth, " ms", 54.,
"Retune time for slides and bends. New notes always retune at once.");
knob(&mut c[6], state, "Gate", &mut settings.gate, -60. ..=-20., d.gate, " dB", 54.,
"Input level that counts as a note. Raise it if tails retrigger.");
knob(&mut c[7], state, "Mix", &mut settings.mix, 0. ..=100., d.mix, " %", 54.,
let last = if advanced {
let mut harmonics = settings.harmonics as f32;
knob(&mut c[4], state, "Harmonics", &mut harmonics, 8. ..=32., d.harmonics as f32, "", 54.,
"How many harmonics are shaped. Higher harmonics pass unchanged.");
settings.harmonics = harmonics.round() as u32;
knob(&mut c[5], state, "Smooth", &mut settings.smooth, 0. ..=100., d.smooth, " ms", 54.,
"Retune time for slides and bends. New notes always retune at once.");
knob(&mut c[6], state, "Gate", &mut settings.gate, -60. ..=-20., d.gate, " dB", 54.,
"Input level that counts as a note. Raise it if tails retrigger.");
7
} else {
4
};
knob(&mut c[last], state, "Mix", &mut settings.mix, 0. ..=100., d.mix, " %", 54.,
"Blend with the delayed dry signal.");
knob(&mut c[8], state, "Output", &mut settings.output, -12. ..=12., d.output, " dB", 54.,
knob(&mut c[last + 1], state, "Output", &mut settings.output, -12. ..=12., d.output, " dB", 54.,
"Output trim.");
});
}