This commit is contained in:
@@ -22,7 +22,7 @@ pub struct ExecutionTrace {
|
||||
pub selected_spans: Vec<SourceSpan>,
|
||||
}
|
||||
|
||||
pub struct StepContext {
|
||||
pub struct StepContext<'a> {
|
||||
pub step: usize,
|
||||
pub beat: f64,
|
||||
pub bank: usize,
|
||||
@@ -36,6 +36,8 @@ pub struct StepContext {
|
||||
pub fill: bool,
|
||||
pub nudge_secs: f64,
|
||||
pub cc_access: Option<Arc<dyn CcAccess>>,
|
||||
pub speed_key: &'a str,
|
||||
pub chain_key: &'a str,
|
||||
#[cfg(feature = "desktop")]
|
||||
pub mouse_x: f64,
|
||||
#[cfg(feature = "desktop")]
|
||||
@@ -44,7 +46,7 @@ pub struct StepContext {
|
||||
pub mouse_down: f64,
|
||||
}
|
||||
|
||||
impl StepContext {
|
||||
impl StepContext<'_> {
|
||||
pub fn step_duration(&self) -> f64 {
|
||||
60.0 / self.tempo / 4.0 / self.speed
|
||||
}
|
||||
@@ -138,6 +140,14 @@ pub(super) struct CmdRegister {
|
||||
}
|
||||
|
||||
impl CmdRegister {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
sound: None,
|
||||
params: Vec::with_capacity(16),
|
||||
deltas: Vec::with_capacity(4),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn set_sound(&mut self, val: Value) {
|
||||
self.sound = Some(val);
|
||||
}
|
||||
|
||||
@@ -70,8 +70,8 @@ impl Forth {
|
||||
trace: Option<&mut ExecutionTrace>,
|
||||
) -> Result<Vec<String>, String> {
|
||||
let mut stack = self.stack.lock().unwrap();
|
||||
let mut outputs: Vec<String> = Vec::new();
|
||||
let mut cmd = CmdRegister::default();
|
||||
let mut outputs: Vec<String> = Vec::with_capacity(8);
|
||||
let mut cmd = CmdRegister::new();
|
||||
|
||||
self.execute_ops(ops, ctx, &mut stack, &mut outputs, &mut cmd, trace)?;
|
||||
|
||||
@@ -148,8 +148,8 @@ impl Forth {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let start = stack.len() - count;
|
||||
let values: Vec<Value> = stack.drain(start..).collect();
|
||||
let selected = values[idx].clone();
|
||||
let selected = stack[start + idx].clone();
|
||||
stack.truncate(start);
|
||||
select_and_run(selected, stack, outputs, cmd)
|
||||
};
|
||||
|
||||
@@ -718,11 +718,10 @@ impl Forth {
|
||||
Op::SetSpeed => {
|
||||
let speed = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let clamped = speed.clamp(0.125, 8.0);
|
||||
let key = format!("__speed_{}_{}__", ctx.bank, ctx.pattern);
|
||||
self.vars
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(key, Value::Float(clamped, None));
|
||||
.insert(ctx.speed_key.to_string(), Value::Float(clamped, None));
|
||||
}
|
||||
|
||||
Op::Chain => {
|
||||
@@ -734,9 +733,10 @@ impl Forth {
|
||||
if bank as usize == ctx.bank && pattern as usize == ctx.pattern {
|
||||
// chaining to self is a no-op
|
||||
} else {
|
||||
let key = format!("__chain_{}_{}__", ctx.bank, ctx.pattern);
|
||||
let val = format!("{bank}:{pattern}");
|
||||
self.vars.lock().unwrap().insert(key, Value::Str(Arc::from(val), None));
|
||||
use std::fmt::Write;
|
||||
let mut val = String::with_capacity(8);
|
||||
let _ = write!(&mut val, "{bank}:{pattern}");
|
||||
self.vars.lock().unwrap().insert(ctx.chain_key.to_string(), Value::Str(Arc::from(val), None));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1010,36 +1010,57 @@ fn emit_output(
|
||||
nudge_secs: f64,
|
||||
outputs: &mut Vec<String>,
|
||||
) {
|
||||
let mut pairs: Vec<(String, String)> = if let Some(s) = sound {
|
||||
vec![("sound".into(), s.to_string())]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
pairs.extend(params.iter().cloned());
|
||||
if nudge_secs > 0.0 {
|
||||
pairs.push(("delta".into(), nudge_secs.to_string()));
|
||||
use std::fmt::Write;
|
||||
let mut out = String::with_capacity(128);
|
||||
out.push('/');
|
||||
|
||||
let has_dur = params.iter().any(|(k, _)| k == "dur");
|
||||
let delaytime_idx = params.iter().position(|(k, _)| k == "delaytime");
|
||||
|
||||
if let Some(s) = sound {
|
||||
let _ = write!(&mut out, "sound/{s}");
|
||||
}
|
||||
// Only add default dur if there's a sound (new voice)
|
||||
if sound.is_some() && !pairs.iter().any(|(k, _)| k == "dur") {
|
||||
pairs.push(("dur".into(), step_duration.to_string()));
|
||||
}
|
||||
// Only add default delaytime if there's a sound (new voice)
|
||||
if sound.is_some() {
|
||||
if let Some(idx) = pairs.iter().position(|(k, _)| k == "delaytime") {
|
||||
let ratio: f64 = pairs[idx].1.parse().unwrap_or(1.0);
|
||||
pairs[idx].1 = (ratio * step_duration).to_string();
|
||||
} else {
|
||||
pairs.push(("delaytime".into(), step_duration.to_string()));
|
||||
|
||||
for (i, (k, v)) in params.iter().enumerate() {
|
||||
if !out.ends_with('/') {
|
||||
out.push('/');
|
||||
}
|
||||
}
|
||||
for pair in &mut pairs {
|
||||
if is_tempo_scaled_param(&pair.0) {
|
||||
if let Ok(val) = pair.1.parse::<f64>() {
|
||||
pair.1 = (val * step_duration).to_string();
|
||||
if is_tempo_scaled_param(k) {
|
||||
if let Ok(val) = v.parse::<f64>() {
|
||||
let _ = write!(&mut out, "{k}/{}", val * step_duration);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if Some(i) == delaytime_idx && sound.is_some() {
|
||||
let ratio: f64 = v.parse().unwrap_or(1.0);
|
||||
let _ = write!(&mut out, "{k}/{}", ratio * step_duration);
|
||||
} else {
|
||||
let _ = write!(&mut out, "{k}/{v}");
|
||||
}
|
||||
}
|
||||
outputs.push(format_cmd(&pairs));
|
||||
|
||||
if nudge_secs > 0.0 {
|
||||
if !out.ends_with('/') {
|
||||
out.push('/');
|
||||
}
|
||||
let _ = write!(&mut out, "delta/{nudge_secs}");
|
||||
}
|
||||
|
||||
if sound.is_some() && !has_dur {
|
||||
if !out.ends_with('/') {
|
||||
out.push('/');
|
||||
}
|
||||
let _ = write!(&mut out, "dur/{step_duration}");
|
||||
}
|
||||
|
||||
if sound.is_some() && delaytime_idx.is_none() {
|
||||
if !out.ends_with('/') {
|
||||
out.push('/');
|
||||
}
|
||||
let _ = write!(&mut out, "delaytime/{step_duration}");
|
||||
}
|
||||
|
||||
outputs.push(out);
|
||||
}
|
||||
|
||||
fn perlin_grad(hash_input: i64) -> f64 {
|
||||
@@ -1116,11 +1137,6 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_cmd(pairs: &[(String, String)]) -> String {
|
||||
let parts: Vec<String> = pairs.iter().map(|(k, v)| format!("{k}/{v}")).collect();
|
||||
format!("/{}", parts.join("/"))
|
||||
}
|
||||
|
||||
fn resolve_cycling(val: &Value, emit_idx: usize) -> Cow<'_, Value> {
|
||||
match val {
|
||||
Value::CycleList(items) if !items.is_empty() => {
|
||||
|
||||
Reference in New Issue
Block a user