Feat: entretien de la codebase

This commit is contained in:
2026-02-09 21:12:49 +01:00
parent 60bc7618d3
commit 80c392c24b
25 changed files with 847 additions and 878 deletions

View File

@@ -98,6 +98,7 @@ pub enum Op {
ClearCmd,
SetSpeed,
At,
Arp,
IntRange,
StepRange,
Generate,

View File

@@ -88,6 +88,7 @@ pub enum Value {
Str(Arc<str>, Option<SourceSpan>),
Quotation(Arc<[Op]>, Option<SourceSpan>),
CycleList(Arc<[Value]>),
ArpList(Arc<[Value]>),
}
impl PartialEq for Value {
@@ -98,6 +99,7 @@ impl PartialEq for Value {
(Value::Str(a, _), Value::Str(b, _)) => a == b,
(Value::Quotation(a, _), Value::Quotation(b, _)) => a == b,
(Value::CycleList(a), Value::CycleList(b)) => a == b,
(Value::ArpList(a), Value::ArpList(b)) => a == b,
_ => false,
}
}
@@ -133,7 +135,7 @@ impl Value {
Value::Float(f, _) => *f != 0.0,
Value::Str(s, _) => !s.is_empty(),
Value::Quotation(..) => true,
Value::CycleList(items) => !items.is_empty(),
Value::CycleList(items) | Value::ArpList(items) => !items.is_empty(),
}
}
@@ -143,14 +145,14 @@ impl Value {
Value::Float(f, _) => f.to_string(),
Value::Str(s, _) => s.to_string(),
Value::Quotation(..) => String::new(),
Value::CycleList(_) => String::new(),
Value::CycleList(_) | Value::ArpList(_) => String::new(),
}
}
pub(super) fn span(&self) -> Option<SourceSpan> {
match self {
Value::Int(_, s) | Value::Float(_, s) | Value::Str(_, s) | Value::Quotation(_, s) => *s,
Value::CycleList(_) => None,
Value::CycleList(_) | Value::ArpList(_) => None,
}
}
}

View File

@@ -216,6 +216,28 @@ impl Forth {
sound_len.max(param_max)
};
let has_arp_list = |cmd: &CmdRegister| -> bool {
matches!(cmd.sound(), Some(Value::ArpList(_)))
|| 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,
emit_idx: usize,
delta_secs: f64,
@@ -231,7 +253,7 @@ impl Forth {
.iter()
.map(|(k, v)| {
let resolved = resolve_cycling(v, emit_idx);
if let Value::CycleList(_) = v {
if let Value::CycleList(_) | Value::ArpList(_) = v {
if let Some(span) = resolved.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
trace.selected_spans.push(span);
@@ -559,24 +581,36 @@ impl Forth {
}
Op::Emit => {
let poly_count = compute_poly_count(cmd);
let deltas = if cmd.deltas().is_empty() {
vec![Value::Float(0.0, None)]
} else {
cmd.deltas().to_vec()
};
if has_arp_list(cmd) {
let arp_count = compute_arp_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 poly_idx in 0..poly_count {
for delta_val in deltas.iter() {
let delta_frac = delta_val.as_float()?;
let delta_secs = ctx.nudge_secs + delta_frac * ctx.step_duration();
if let Some(span) = delta_val.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
trace.selected_spans.push(span);
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() {
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()
};
if let Some(sound_val) =
emit_with_cycling(cmd, poly_idx, delta_secs, outputs)?
emit_with_cycling(cmd, i, delta_secs, outputs)?
{
if let Some(span) = sound_val.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
@@ -585,6 +619,37 @@ impl Forth {
}
}
}
} else {
let poly_count = compute_poly_count(cmd);
let deltas = if cmd.deltas().is_empty() {
vec![Value::Float(0.0, None)]
} else {
cmd.deltas().to_vec()
};
for poly_idx in 0..poly_count {
for delta_val in deltas.iter() {
let delta_frac = delta_val.as_float()?;
let delta_secs =
ctx.nudge_secs + delta_frac * ctx.step_duration();
if let Some(span) = delta_val.span() {
if let Some(trace) = trace_cell.borrow_mut().as_mut() {
trace.selected_spans.push(span);
}
}
if let Some(sound_val) =
emit_with_cycling(cmd, poly_idx, 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);
}
}
}
}
}
}
}
@@ -976,6 +1041,14 @@ impl Forth {
cmd.set_deltas(deltas);
}
Op::Arp => {
if stack.is_empty() {
return Err("stack underflow".into());
}
let values = std::mem::take(stack);
stack.push(Value::ArpList(Arc::from(values)));
}
Op::Adsr => {
let r = stack.pop().ok_or("stack underflow")?;
let s = stack.pop().ok_or("stack underflow")?;
@@ -1528,7 +1601,7 @@ where
fn resolve_cycling(val: &Value, emit_idx: usize) -> Cow<'_, Value> {
match val {
Value::CycleList(items) if !items.is_empty() => {
Value::CycleList(items) | Value::ArpList(items) if !items.is_empty() => {
Cow::Owned(items[emit_idx % items.len()].clone())
}
other => Cow::Borrowed(other),

View File

@@ -79,6 +79,7 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
"tempo!" => Op::SetTempo,
"speed!" => Op::SetSpeed,
"at" => Op::At,
"arp" => Op::Arp,
"adsr" => Op::Adsr,
"ad" => Op::Ad,
"apply" => Op::Apply,

View File

@@ -23,6 +23,16 @@ pub(super) const WORDS: &[Word] = &[
compile: Simple,
varargs: false,
},
Word {
name: "arp",
aliases: &[],
category: "Sound",
stack: "(v1..vn -- arplist)",
desc: "Wrap stack values as arpeggio list for spreading across deltas",
example: "c4 e4 g4 b4 arp note => arpeggio",
compile: Simple,
varargs: true,
},
Word {
name: "clear",
aliases: &[],