diff --git a/src/lib.rs b/src/lib.rs index fb6472e..0cd8bcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ use nih_plug_egui::{create_egui_editor, egui, widgets, EguiState}; use std::sync::Arc; // ────────────────────────────────────────────── -// Params — time, feedback, mix +// Params — time, feedback, mix, sync // ────────────────────────────────────────────── const MAX_DELAY_MS: f32 = 2_000.0; @@ -16,7 +16,7 @@ struct KolmekiviParams { #[persist = "editor-state"] editor_state: Arc, - /// Delay time in milliseconds. + /// Delay time in milliseconds (sync off) or beat subdivision (sync on). #[id = "time"] time: FloatParam, @@ -27,12 +27,17 @@ struct KolmekiviParams { /// Wet/dry mix (0 = dry only, 1 = wet only). #[id = "mix"] mix: FloatParam, + + /// When true, Time is interpreted as a beat subdivision (1.0 = quarter note) + /// derived from the host tempo. When false, Time is raw milliseconds. + #[id = "sync"] + sync: BoolParam, } impl Default for KolmekiviParams { fn default() -> Self { Self { - editor_state: EguiState::from_size(320, 160), + editor_state: EguiState::from_size(320, 180), time: FloatParam::new( "Time", 500.0, @@ -54,6 +59,8 @@ impl Default for KolmekiviParams { FloatRange::Linear { min: 0.0, max: 1.0 }, ) .with_smoother(SmoothingStyle::Logarithmic(32.0)), + + sync: BoolParam::new("Sync", false), } } } @@ -168,9 +175,14 @@ impl Plugin for Kolmekivi { (), |_, _| {}, move |egui_ctx, setter, _state| { + let sync = params.sync.value(); egui::CentralPanel::default().show(egui_ctx, |ui| { ui.horizontal(|ui| { - ui.label("Time"); + if sync { + ui.label("Time (1/n)"); + } else { + ui.label("Time"); + } ui.add(widgets::ParamSlider::for_param(¶ms.time, setter)); }); ui.horizontal(|ui| { @@ -181,6 +193,14 @@ impl Plugin for Kolmekivi { ui.label("Mix"); ui.add(widgets::ParamSlider::for_param(¶ms.mix, setter)); }); + ui.horizontal(|ui| { + let mut sync_val = params.sync.value(); + let changed = ui.checkbox(&mut sync_val as &mut bool, "Sync to tempo"); + if changed.changed() { + setter.begin_set_parameter(¶ms.sync); + setter.set_parameter(¶ms.sync, sync_val); + } + }); }); }, ) @@ -222,7 +242,20 @@ impl Plugin for Kolmekivi { let time_val = self.params.time.smoothed.next(); let feedback = self.params.feedback.smoothed.next(); let mix = self.params.mix.smoothed.next(); - let delay_samples = (time_val * sample_rate / 1000.0) as usize; + let sync = self.params.sync.value(); + + let delay_samples = if sync { + // Time is a beat subdivision multiplier: 1.0 = quarter note, + // 0.5 = eighth, 2.0 = half note, etc. + let tempo = _context.transport().tempo.unwrap_or(120.0) as f32; + let beat_duration_s = 60.0_f32 / tempo; // quarter note in seconds + let subdivisions = time_val.clamp(0.015625, 8.0); // 32nd to 4x whole + let delay_s = subdivisions * beat_duration_s; + (delay_s * sample_rate) as usize + } else { + // Time is raw milliseconds + (time_val * sample_rate / 1000.0) as usize + }; // Process each sample in the buffer for mut frame in buffer.iter_samples() {