Files
codename-206/src/editor/mod.rs
T
Mikkeli Matlock 42c5f7dcd2 feat: per-channel dry/wet mix (parallel compression) replacing bypass
Replace the per-channel bypass toggle with a smoothed dry/wet `mix` (0..100%,
default 100%). The blend is applied at the compressor output:
  out = delayed_input * ((1 - mix) + mix * wet_gain)
Dry and wet share the same delayed input, so it's phase-aligned (parallel
compression, no comb filtering). mix=0 is bit-identical to the old bypass.

The detector now runs even at mix 0, so the GR meter shows the wet gain
reduction regardless of mix, while the level/plot out trace reads the mixed
output. "Bands at 0% = simple full-band comp via All" still holds.

Update README + parameter docs (bypass -> mix).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:25:38 +09:00

106 lines
4.8 KiB
Rust

//! egui editor: assembly + control layout.
//!
//! The aggregator. Builds the editor window and lays out the heading, the meter panel
//! ([`meter`]), the rolling plot ([`plot`]), and the (placeholder) per-channel slider columns.
//! Each visualiser owns its GUI state and drawing in its submodule; this module wires them
//! together and holds the shared [`EditorState`]. When the UI is redesigned the slider columns
//! get replaced and the visualisers stay as self-contained widgets.
use nih_plug::prelude::*;
use nih_plug_egui::{
create_egui_editor,
egui::{self, Vec2},
resizable_window::ResizableWindow,
widgets,
};
use std::sync::Arc;
use crate::meters::Meters;
use crate::params::{Codename206Params, CompressorParams};
use crate::Codename206;
mod crossover;
mod meter;
mod plot;
/// Bottom of the dB scale shared by the meters and the plot (top is 0 dBFS).
const METER_FLOOR_DB: f32 = -60.0;
/// GUI-side editor state (not persisted): the per-widget state for the meter panel and the plot.
#[derive(Default)]
struct EditorState {
meter: meter::MeterState,
plot: plot::PlotState,
}
/// Build the plugin editor over shared handles to the params and meter state.
pub(crate) fn create(params: Arc<Codename206Params>, meters: Arc<Meters>) -> Option<Box<dyn Editor>> {
let egui_state = params.editor_state.clone();
create_egui_editor(
params.editor_state.clone(),
EditorState::default(),
|_, _| {},
move |egui_ctx, setter, state| {
// Keep frames coming so the meters animate and the lamp can time out while open.
egui_ctx.request_repaint();
// One column of controls for a single compressor channel (placeholder layout).
let band_col = |ui: &mut egui::Ui, title: &str, p: &CompressorParams| {
ui.strong(title);
ui.label("Pre-gain");
ui.add(widgets::ParamSlider::for_param(&p.pre_gain_db, setter));
ui.add(widgets::ParamSlider::for_param(&p.detection, setter));
ui.label("Threshold");
ui.add(widgets::ParamSlider::for_param(&p.threshold_db, setter));
ui.label("Ratio");
ui.add(widgets::ParamSlider::for_param(&p.ratio, setter));
ui.label("Knee");
ui.add(widgets::ParamSlider::for_param(&p.knee_db, setter));
ui.label("Attack");
ui.add(widgets::ParamSlider::for_param(&p.attack_ms, setter));
ui.label("Release");
ui.add(widgets::ParamSlider::for_param(&p.release_ms, setter));
ui.label("Makeup");
ui.add(widgets::ParamSlider::for_param(&p.makeup_db, setter));
ui.label("Mix");
ui.add(widgets::ParamSlider::for_param(&p.mix, setter));
};
// Resizable window; vertical scroll so every control stays reachable even when the
// window is small. (Placeholder layout — the redesign will replace the slider columns.)
ResizableWindow::new("editor")
.min_size(Vec2::new(480.0, 320.0))
.show(egui_ctx, egui_state.as_ref(), |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
ui.heading(Codename206::NAME);
meter::draw(ui, &meters, &mut state.meter);
ui.separator();
plot::draw(ui, &meters, &mut state.plot);
ui.separator();
crossover::draw(ui, &params, setter);
ui.separator();
// Global controls stacked vertically so they never overflow sideways.
egui::Grid::new("globals").num_columns(2).show(ui, |ui| {
ui.label("Look-ahead");
ui.add(widgets::ParamSlider::for_param(&params.look_ahead_ms, setter));
ui.end_row();
ui.label("Ceiling");
ui.add(widgets::ParamSlider::for_param(&params.output_ceiling_db, setter));
ui.end_row();
ui.label("Lim Release");
ui.add(widgets::ParamSlider::for_param(&params.limiter_release_ms, setter));
ui.end_row();
});
ui.separator();
ui.columns(4, |cols| {
band_col(&mut cols[0], "LOW", &params.low);
band_col(&mut cols[1], "MID", &params.mid);
band_col(&mut cols[2], "HIGH", &params.high);
band_col(&mut cols[3], "ALL", &params.all);
});
});
});
},
)
}