feat: add tempo-sync for delay time
Add a Sync checkbox parameter. When enabled the Time parameter is interpreted as a beat subdivision (1.0 = quarter note, 0.5 = eighth, 2.0 = half note) derived from the host transport tempo. Falls back to 120 BPM when no tempo is available. The Time label shows 'Time (1/n)' in the UI when sync is on for visual clarity.
This commit is contained in:
+37
-4
@@ -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<EguiState>,
|
||||
|
||||
/// 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| {
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user