Feat: improve 'at' in cagire grammar

This commit is contained in:
2026-03-20 23:29:47 +01:00
parent 609fe108bc
commit f020b5a172
13 changed files with 293 additions and 277 deletions

View File

@@ -241,31 +241,7 @@ impl Forth {
sound_len.max(param_max)
};
let has_arp_list = |cmd: &CmdRegister| -> bool {
matches!(cmd.sound(), Some(Value::ArpList(_)))
|| cmd.global_params().iter().chain(cmd.params().iter())
.any(|(_, v)| matches!(v, Value::ArpList(_)))
};
let compute_arp_count = |cmd: &CmdRegister| -> usize {
let sound_len = match cmd.sound() {
Some(Value::ArpList(items)) => items.len(),
_ => 0,
};
let param_max = cmd
.params()
.iter()
.map(|(_, v)| match v {
Value::ArpList(items) => items.len(),
_ => 0,
})
.max()
.unwrap_or(0);
sound_len.max(param_max).max(1)
};
let emit_with_cycling = |cmd: &CmdRegister,
arp_idx: usize,
poly_idx: usize,
delta_secs: f64,
outputs: &mut Vec<String>|
@@ -277,7 +253,7 @@ impl Forth {
return Err("nothing to emit".into());
}
let resolved_sound_val =
cmd.sound().map(|sv| resolve_value(sv, arp_idx, poly_idx));
cmd.sound().map(|sv| resolve_value(sv, poly_idx));
let sound_str = match &resolved_sound_val {
Some(v) => Some(v.as_str()?.to_string()),
None => None,
@@ -286,8 +262,8 @@ impl Forth {
.iter()
.chain(cmd.params().iter())
.map(|(k, v)| {
let resolved = resolve_value(v, arp_idx, poly_idx);
if let Value::CycleList(_) | Value::ArpList(_) = v {
let resolved = resolve_value(v, poly_idx);
if let Value::CycleList(_) = v {
if let Some(span) = resolved.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
trace.selected_spans.push(span);
@@ -595,47 +571,17 @@ impl Forth {
}
Op::Emit => {
if has_arp_list(cmd) {
let arp_count = compute_arp_count(cmd);
if let Some(dsecs) = cmd.take_delta_secs() {
let poly_count = compute_poly_count(cmd);
let explicit_deltas = !cmd.deltas().is_empty();
let delta_list: Vec<Value> = if explicit_deltas {
cmd.deltas().to_vec()
} else {
Vec::new()
};
let count = if explicit_deltas {
arp_count.max(delta_list.len())
} else {
arp_count
};
for i in 0..count {
let delta_secs = if explicit_deltas {
let dv = &delta_list[i % delta_list.len()];
let frac = dv.as_float()?;
if let Some(span) = dv.span() {
for poly_idx in 0..poly_count {
if let Some(sound_val) =
emit_with_cycling(cmd, poly_idx, dsecs, outputs)?
{
if let Some(span) = sound_val.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
trace.selected_spans.push(span);
}
}
ctx.nudge_secs + frac * ctx.step_duration()
} else {
ctx.nudge_secs
+ (i as f64 / count as f64) * ctx.step_duration()
};
for poly_i in 0..poly_count {
if let Some(sound_val) =
emit_with_cycling(cmd, i, poly_i, delta_secs, outputs)?
{
if let Some(span) = sound_val.span() {
if let Some(trace) =
trace_cell.borrow_mut().as_mut()
{
trace.selected_spans.push(span);
}
}
}
}
}
} else {
@@ -657,7 +603,7 @@ impl Forth {
}
}
if let Some(sound_val) =
emit_with_cycling(cmd, 0, poly_idx, delta_secs, outputs)?
emit_with_cycling(cmd, poly_idx, delta_secs, outputs)?
{
if let Some(span) = sound_val.span() {
if let Some(trace) =
@@ -1196,12 +1142,60 @@ impl Forth {
cmd.set_deltas(deltas);
}
Op::Arp => {
Op::AtLoop(body_ops) => {
ensure(stack, 1)?;
let values = std::mem::take(stack);
stack.push(Value::ArpList(Arc::from(values)));
let deltas = std::mem::take(stack);
let n = deltas.len();
for (i, delta_val) in deltas.iter().enumerate() {
let frac = delta_val.as_float()?;
let delta_secs = ctx.nudge_secs + frac * ctx.step_duration();
let iter_ctx = StepContext {
step: ctx.step,
beat: ctx.beat,
bank: ctx.bank,
pattern: ctx.pattern,
tempo: ctx.tempo,
phase: ctx.phase,
slot: ctx.slot,
runs: ctx.runs * n + i,
iter: ctx.iter,
speed: ctx.speed,
fill: ctx.fill,
nudge_secs: ctx.nudge_secs,
sr: ctx.sr,
cc_access: ctx.cc_access,
speed_key: ctx.speed_key,
mouse_x: ctx.mouse_x,
mouse_y: ctx.mouse_y,
mouse_down: ctx.mouse_down,
};
cmd.set_delta_secs(delta_secs);
let mut trace_opt = trace_cell.borrow_mut().take();
let mut var_writes_guard = var_writes_cell.borrow_mut();
let vw = var_writes_guard.as_mut().expect("var_writes taken");
self.execute_ops(
body_ops,
&iter_ctx,
stack,
outputs,
cmd,
trace_opt.as_deref_mut(),
vars_snapshot,
vw,
)?;
drop(var_writes_guard);
*trace_cell.borrow_mut() = trace_opt;
cmd.clear_params();
cmd.clear_sound();
}
}
Op::Adsr => {
let r = pop(stack)?;
let s = pop(stack)?;
@@ -1499,35 +1493,13 @@ impl Forth {
// MIDI operations
Op::MidiEmit => {
let at_loop_delta = cmd.take_delta_secs();
let (_, params) = cmd.snapshot().unwrap_or((None, &[]));
// Build schedule: (arp_idx, poly_idx, delta_secs)
let schedule: Vec<(usize, usize, f64)> = if has_arp_list(cmd) {
let arp_count = compute_arp_count(cmd);
// Build schedule: (poly_idx, delta_secs)
let schedule: Vec<(usize, f64)> = if let Some(dsecs) = at_loop_delta {
let poly_count = compute_poly_count(cmd);
let explicit = !cmd.deltas().is_empty();
let delta_list = cmd.deltas();
let count = if explicit {
arp_count.max(delta_list.len())
} else {
arp_count
};
let mut sched = Vec::with_capacity(count * poly_count);
for i in 0..count {
let delta_secs = if explicit {
let frac = delta_list[i % delta_list.len()]
.as_float()
.unwrap_or(0.0);
ctx.nudge_secs + frac * ctx.step_duration()
} else {
ctx.nudge_secs
+ (i as f64 / count as f64) * ctx.step_duration()
};
for poly_i in 0..poly_count {
sched.push((i, poly_i, delta_secs));
}
}
sched
(0..poly_count).map(|pi| (pi, dsecs)).collect()
} else {
let poly_count = compute_poly_count(cmd);
let deltas: Vec<f64> = if cmd.deltas().is_empty() {
@@ -1542,7 +1514,6 @@ impl Forth {
for poly_idx in 0..poly_count {
for &frac in &deltas {
sched.push((
0,
poly_idx,
ctx.nudge_secs + frac * ctx.step_duration(),
));
@@ -1551,14 +1522,14 @@ impl Forth {
sched
};
for (arp_idx, poly_idx, delta_secs) in schedule {
for (poly_idx, delta_secs) in schedule {
let get_int = |name: &str| -> Option<i64> {
params
.iter()
.rev()
.find(|(k, _)| *k == name)
.and_then(|(_, v)| {
resolve_value(v, arp_idx, poly_idx).as_int().ok()
resolve_value(v, poly_idx).as_int().ok()
})
};
let get_float = |name: &str| -> Option<f64> {
@@ -1567,7 +1538,7 @@ impl Forth {
.rev()
.find(|(k, _)| *k == name)
.and_then(|(_, v)| {
resolve_value(v, arp_idx, poly_idx).as_float().ok()
resolve_value(v, poly_idx).as_float().ok()
})
};
let chan = get_int("chan")
@@ -1960,10 +1931,6 @@ where
F: Fn(f64) -> f64 + Copy,
{
match val {
Value::ArpList(items) => {
let mapped: Result<Vec<_>, _> = items.iter().map(|x| lift_unary(x, f)).collect();
Ok(Value::ArpList(Arc::from(mapped?)))
}
Value::CycleList(items) => {
let mapped: Result<Vec<_>, _> = items.iter().map(|x| lift_unary(x, f)).collect();
Ok(Value::CycleList(Arc::from(mapped?)))
@@ -1977,11 +1944,6 @@ where
F: Fn(i64) -> i64 + Copy,
{
match val {
Value::ArpList(items) => {
let mapped: Result<Vec<_>, _> =
items.iter().map(|x| lift_unary_int(x, f)).collect();
Ok(Value::ArpList(Arc::from(mapped?)))
}
Value::CycleList(items) => {
let mapped: Result<Vec<_>, _> =
items.iter().map(|x| lift_unary_int(x, f)).collect();
@@ -1996,16 +1958,6 @@ where
F: Fn(f64, f64) -> f64 + Copy,
{
match (a, b) {
(Value::ArpList(items), b) => {
let mapped: Result<Vec<_>, _> =
items.iter().map(|x| lift_binary(x, b, f)).collect();
Ok(Value::ArpList(Arc::from(mapped?)))
}
(a, Value::ArpList(items)) => {
let mapped: Result<Vec<_>, _> =
items.iter().map(|x| lift_binary(a, x, f)).collect();
Ok(Value::ArpList(Arc::from(mapped?)))
}
(Value::CycleList(items), b) => {
let mapped: Result<Vec<_>, _> =
items.iter().map(|x| lift_binary(x, b, f)).collect();
@@ -2045,11 +1997,8 @@ where
Ok(())
}
fn resolve_value(val: &Value, arp_idx: usize, poly_idx: usize) -> Cow<'_, Value> {
fn resolve_value(val: &Value, poly_idx: usize) -> Cow<'_, Value> {
match val {
Value::ArpList(items) if !items.is_empty() => {
Cow::Owned(items[arp_idx % items.len()].clone())
}
Value::CycleList(items) if !items.is_empty() => {
Cow::Owned(items[poly_idx % items.len()].clone())
}