Try to optimize

This commit is contained in:
2026-01-29 11:53:47 +01:00
parent 73db616139
commit 495bfb3bdc
14 changed files with 764 additions and 214 deletions

View File

@@ -83,4 +83,7 @@ pub enum Op {
ClearCmd,
SetSpeed,
At,
IntRange,
Generate,
GeomRange,
}

View File

@@ -140,10 +140,8 @@ impl CmdRegister {
&self.deltas
}
pub(super) fn snapshot(&self) -> Option<(Value, Vec<(String, Value)>)> {
self.sound
.as_ref()
.map(|s| (s.clone(), self.params.clone()))
pub(super) fn snapshot(&self) -> Option<(&Value, &[(String, Value)])> {
self.sound.as_ref().map(|s| (s, self.params.as_slice()))
}
pub(super) fn clear(&mut self) {

View File

@@ -1,5 +1,6 @@
use rand::rngs::StdRng;
use rand::{Rng as RngTrait, SeedableRng};
use std::borrow::Cow;
use super::compiler::compile_script;
use super::ops::Op;
@@ -147,13 +148,11 @@ impl Forth {
let emit_with_cycling = |cmd: &CmdRegister, emit_idx: usize, delta_secs: f64, outputs: &mut Vec<String>| -> Result<Option<Value>, String> {
let (sound_val, params) = cmd.snapshot().ok_or("no sound set")?;
let resolved_sound_val = resolve_cycling(&sound_val, emit_idx);
// Note: sound span is recorded by Op::Emit, not here
let resolved_sound_val = resolve_cycling(sound_val, emit_idx);
let sound = resolved_sound_val.as_str()?.to_string();
let resolved_params: Vec<(String, String)> =
params.iter().map(|(k, v)| {
let resolved = resolve_cycling(v, emit_idx);
// Record selected span for params if they came from a CycleList
if let Value::CycleList(_) = v {
if let Some(span) = resolved.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
@@ -164,7 +163,7 @@ impl Forth {
(k.clone(), resolved.to_param_string())
}).collect();
emit_output(&sound, &resolved_params, ctx.step_duration(), delta_secs, outputs);
Ok(Some(resolved_sound_val))
Ok(Some(resolved_sound_val.into_owned()))
};
while pc < ops.len() {
@@ -630,8 +629,15 @@ impl Forth {
let top = stack.pop().ok_or("stack underflow")?;
let deltas = match &top {
Value::Float(..) => vec![top],
Value::Int(n, _) if *n > 0 && stack.len() >= *n as usize => {
Value::Int(n, _) => {
let count = *n as usize;
if stack.len() < count {
return Err(format!(
"at: stack underflow, expected {} values but got {}",
count,
stack.len()
));
}
let mut vals = Vec::with_capacity(count);
for _ in 0..count {
vals.push(stack.pop().ok_or("stack underflow")?);
@@ -639,8 +645,7 @@ impl Forth {
vals.reverse();
vals
}
Value::Int(..) => vec![top],
_ => return Err("at expects number or list".into()),
_ => return Err("at expects float or int count".into()),
};
cmd.set_deltas(deltas);
}
@@ -709,6 +714,50 @@ impl Forth {
emit_with_cycling(cmd, i, ctx.nudge_secs, outputs)?;
}
}
Op::IntRange => {
let end = stack.pop().ok_or("stack underflow")?.as_int()?;
let start = stack.pop().ok_or("stack underflow")?.as_int()?;
if start <= end {
for i in start..=end {
stack.push(Value::Int(i, None));
}
} else {
for i in (end..=start).rev() {
stack.push(Value::Int(i, None));
}
}
}
Op::Generate => {
let count = stack.pop().ok_or("stack underflow")?.as_int()?;
let quot = stack.pop().ok_or("stack underflow")?;
if count < 0 {
return Err("gen count must be >= 0".into());
}
let mut results = Vec::with_capacity(count as usize);
for _ in 0..count {
run_quotation(quot.clone(), stack, outputs, cmd)?;
results.push(stack.pop().ok_or("gen: quotation must produce a value")?);
}
for val in results {
stack.push(val);
}
}
Op::GeomRange => {
let count = stack.pop().ok_or("stack underflow")?.as_int()?;
let ratio = stack.pop().ok_or("stack underflow")?.as_float()?;
let start = stack.pop().ok_or("stack underflow")?.as_float()?;
if count < 0 {
return Err("geom.. count must be >= 0".into());
}
let mut val = start;
for _ in 0..count {
stack.push(float_to_value(val));
val *= ratio;
}
}
}
pc += 1;
}
@@ -717,31 +766,34 @@ impl Forth {
}
}
const TEMPO_SCALED_PARAMS: &[&str] = &[
"attack",
"decay",
"release",
"lpa",
"lpd",
"lpr",
"hpa",
"hpd",
"hpr",
"bpa",
"bpd",
"bpr",
"patt",
"pdec",
"prel",
"fma",
"fmd",
"fmr",
"glide",
"verbdecay",
"verbpredelay",
"chorusdelay",
"duration",
];
fn is_tempo_scaled_param(name: &str) -> bool {
matches!(
name,
"attack"
| "decay"
| "release"
| "lpa"
| "lpd"
| "lpr"
| "hpa"
| "hpd"
| "hpr"
| "bpa"
| "bpd"
| "bpr"
| "patt"
| "pdec"
| "prel"
| "fma"
| "fmd"
| "fmr"
| "glide"
| "verbdecay"
| "verbpredelay"
| "chorusdelay"
| "duration"
)
}
fn emit_output(
sound: &str,
@@ -765,7 +817,7 @@ fn emit_output(
pairs.push(("delaytime".into(), step_duration.to_string()));
}
for pair in &mut pairs {
if TEMPO_SCALED_PARAMS.contains(&pair.0.as_str()) {
if is_tempo_scaled_param(&pair.0) {
if let Ok(val) = pair.1.parse::<f64>() {
pair.1 = (val * step_duration).to_string();
}
@@ -853,11 +905,11 @@ fn format_cmd(pairs: &[(String, String)]) -> String {
format!("/{}", parts.join("/"))
}
fn resolve_cycling(val: &Value, emit_idx: usize) -> Value {
fn resolve_cycling(val: &Value, emit_idx: usize) -> Cow<'_, Value> {
match val {
Value::CycleList(items) if !items.is_empty() => {
items[emit_idx % items.len()].clone()
Cow::Owned(items[emit_idx % items.len()].clone())
}
other => other.clone(),
other => Cow::Borrowed(other),
}
}

File diff suppressed because it is too large Load Diff