Skip to main content

sim_lib_view_daw/
param.rs

1//! Parameter and transport edits: `intent/set-param` and `intent/scrub`.
2//!
3//! These produce the new parameter or transport value to commit through
4//! `realize`; nothing is mutated in place. They back onto the existing DAW and
5//! plugin/synth values: a synth patch is a parameter map, a transport carries a
6//! playhead position.
7
8use sim_kernel::{Error, Expr, Result, Symbol};
9use sim_lib_intent::{field, intent_kind_of};
10
11/// Apply an `intent/set-param` to a parameter map, returning the new map. The
12/// returned value is the operation to commit through `realize`.
13pub fn apply_set_param(params: &Expr, intent: &Expr) -> Result<Expr> {
14    expect_kind(intent, "set-param")?;
15    let param = match field(intent, "param") {
16        Some(Expr::Symbol(symbol)) => symbol.clone(),
17        _ => {
18            return Err(Error::HostError(
19                "set-param 'param' must be a symbol".to_owned(),
20            ));
21        }
22    };
23    let value = field(intent, "value")
24        .cloned()
25        .ok_or_else(|| Error::HostError("set-param is missing a 'value'".to_owned()))?;
26    Ok(set_key(params, &param, value))
27}
28
29/// Apply an `intent/scrub` to a transport value, returning the new transport
30/// with its playhead moved to the requested position.
31pub fn apply_scrub(transport: &Expr, intent: &Expr) -> Result<Expr> {
32    expect_kind(intent, "scrub")?;
33    let at = field(intent, "at")
34        .cloned()
35        .ok_or_else(|| Error::HostError("scrub is missing an 'at'".to_owned()))?;
36    Ok(set_key(transport, &Symbol::new("position"), at))
37}
38
39fn expect_kind(intent: &Expr, kind: &str) -> Result<()> {
40    match intent_kind_of(intent) {
41        Some(symbol) if symbol.name.as_ref() == kind => Ok(()),
42        _ => Err(Error::HostError(format!("expected an intent/{kind}"))),
43    }
44}
45
46fn set_key(map: &Expr, key: &Symbol, value: Expr) -> Expr {
47    let mut entries = match map {
48        Expr::Map(entries) => entries.clone(),
49        _ => Vec::new(),
50    };
51    let matches = |entry_key: &Expr| matches!(entry_key, Expr::Symbol(symbol) if symbol == key);
52    if let Some(slot) = entries.iter_mut().find(|(entry_key, _)| matches(entry_key)) {
53        slot.1 = value;
54    } else {
55        entries.push((Expr::Symbol(key.clone()), value));
56    }
57    Expr::Map(entries)
58}