Dynamic changes to LFO
This commit is contained in:
@@ -86,6 +86,10 @@ struct Module {
|
||||
x: f32,
|
||||
y: f32,
|
||||
param_values: Vec<f32>,
|
||||
/// Per-param CV modulation offset for display only (JS pushes live values
|
||||
/// each frame via `set_cv_mod`). Added to `param_values` then clamped to
|
||||
/// [min, max] before rendering. Reset to 0 each frame via `clear_cv_mods`.
|
||||
cv_mod: Vec<f32>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
@@ -279,7 +283,8 @@ impl PatchBay {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
let param_values = desc.params.iter().map(|p| p.default).collect();
|
||||
self.modules.push(Module { id, kind: desc.kind, label: desc.label, x, y, param_values });
|
||||
let cv_mod = vec![0.0_f32; desc.params.len()];
|
||||
self.modules.push(Module { id, kind: desc.kind, label: desc.label, x, y, param_values, cv_mod });
|
||||
self.patch_version += 1;
|
||||
id
|
||||
} else {
|
||||
@@ -308,6 +313,28 @@ impl PatchBay {
|
||||
self.params_version
|
||||
}
|
||||
|
||||
/// Reset all CV display offsets to zero. Call once per frame before
|
||||
/// pushing fresh values via `set_cv_mod`.
|
||||
pub fn clear_cv_mods(&mut self) {
|
||||
for m in &mut self.modules {
|
||||
for v in &mut m.cv_mod { *v = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a live CV modulation offset (in the param's native unit) for
|
||||
/// display purposes. The offset is added to the knob value and clamped
|
||||
/// to [min, max] before rendering.
|
||||
pub fn set_cv_mod(&mut self, module_id: u32, param_id: &str, offset: f32) {
|
||||
if let Some(m) = self.modules.iter_mut().find(|m| m.id == module_id) {
|
||||
let desc = REGISTRY.iter().find(|d| d.kind == m.kind).expect("unknown kind");
|
||||
if let Some(pi) = desc.params.iter().position(|p| p.id == param_id) {
|
||||
if pi < m.cv_mod.len() {
|
||||
m.cv_mod[pi] = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update a parameter value (param_idx is the position in the descriptor's params slice).
|
||||
pub fn set_param(&mut self, module_id: u32, param_idx: usize, value: f32) {
|
||||
if let Some(m) = self.modules.iter_mut().find(|m| m.id == module_id) {
|
||||
@@ -729,7 +756,9 @@ impl PatchBay {
|
||||
for (pi, p) in desc.params.iter().enumerate() {
|
||||
let is_active = active_param == Some(pi);
|
||||
let py = params_top + 4.0 + pi as f32 * PARAM_ROW;
|
||||
let val = m.param_values.get(pi).copied().unwrap_or(p.default);
|
||||
let base = m.param_values.get(pi).copied().unwrap_or(p.default);
|
||||
let mod_v = m.cv_mod.get(pi).copied().unwrap_or(0.0);
|
||||
let val = (base + mod_v).clamp(p.min, p.max);
|
||||
let tx = m.x + 8.0;
|
||||
let tw = MOD_W - 16.0;
|
||||
|
||||
@@ -802,10 +831,15 @@ impl PatchBay {
|
||||
|
||||
// ── Mini oscilloscope preview (VCO / LFO) ────────────────────────────
|
||||
if has_waveform_preview(desc) {
|
||||
// Effective value = knob + CV modulation offset, clamped to param range.
|
||||
let get_param = |id: &str| -> f32 {
|
||||
desc.params.iter().position(|p| p.id == id)
|
||||
.and_then(|pi| m.param_values.get(pi))
|
||||
.copied()
|
||||
.map(|pi| {
|
||||
let base = m.param_values.get(pi).copied().unwrap_or(0.0);
|
||||
let mod_v = m.cv_mod.get(pi).copied().unwrap_or(0.0);
|
||||
let p = &desc.params[pi];
|
||||
(base + mod_v).clamp(p.min, p.max)
|
||||
})
|
||||
.unwrap_or(0.0)
|
||||
};
|
||||
let wave_idx = get_param("waveform").round() as usize;
|
||||
|
||||
Reference in New Issue
Block a user