refactor: run Lathe, Temper and Knurl as tracked modules

Add a Tracked trait and a Lookahead harness to common-dsp. Each engine is
now its tracker plus a module that follows note events aligned with its
input, so the same module can run behind a shared tracker.

Lathe and Temper output is bit-exact against the old engines. Knurl's
interpolation now reads one newer sample inside the module, so its
latency is one sample longer; the output matches within float rounding.

Each editor exposes body() and toggles() besides show(), so another
editor can draw a module's page.
This commit is contained in:
nvrl committed 2026-11-06 16:30:22 +01:00
1 parent 4b1a89626e
commit 340376eee2
8 files changed
+418 -163

No files matched your search

+20 -10
View File
@@ -233,14 +233,7 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
if r.clicked() {
settings.bypass = !settings.bypass;
}
let r = ui
.selectable_label(settings.auto_gain, RichText::new("AUTO GAIN").size(13.))
.on_hover_text(
"Match output loudness to the input, so Drive changes character, not level.",
);
if r.clicked() {
settings.auto_gain = !settings.auto_gain;
}
toggles(ui, settings);
ui.separator();
for (source, label) in [(Source::Midi, "MIDI"), (Source::Audio, "AUDIO")] {
let r = ui.selectable_label(settings.source == source, RichText::new(label).size(13.));
@@ -250,6 +243,13 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
}
caption(ui, "PITCH FROM");
});
body(ui, settings, state);
ui.separator();
ui.label(RichText::new(&state.help).color(MUTED));
}
/// Everything under the header: the standalone editor and Forge both draw it.
pub fn body(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
ui.add_space(6.);
let frame = state.analyzer;
ui.horizontal_top(|ui| {
@@ -294,8 +294,6 @@ pub fn show(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
ui.set_width(ui.available_width());
ui.vertical(|ui| controls(ui, settings, state));
});
ui.separator();
ui.label(RichText::new(&state.help).color(MUTED));
}
const TOP: f32 = 290.;
@@ -411,3 +409,15 @@ fn controls(ui: &mut egui::Ui, settings: &mut Settings, state: &mut State) {
"Output trim.");
});
}
/// Header switches besides Bypass and the pitch source.
pub fn toggles(ui: &mut egui::Ui, settings: &mut Settings) {
let r = ui
.selectable_label(settings.auto_gain, RichText::new("AUTO GAIN").size(13.))
.on_hover_text(
"Match output loudness to the input, so Drive changes character, not level.",
);
if r.clicked() {
settings.auto_gain = !settings.auto_gain;
}
}