Feat: extend CI to cover desktop

This commit is contained in:
2026-01-30 21:19:48 +01:00
parent 4c635500dd
commit 4049c7787c
5 changed files with 61 additions and 17 deletions

View File

@@ -41,6 +41,7 @@ pub type Variables = Arc<Mutex<HashMap<String, Value>>>;
pub type Dictionary = Arc<Mutex<HashMap<String, Vec<Op>>>>;
pub type Rng = Arc<Mutex<StdRng>>;
pub type Stack = Arc<Mutex<Vec<Value>>>;
pub(super) type CmdSnapshot<'a> = (Option<&'a Value>, &'a [(String, Value)]);
#[derive(Clone, Debug)]
pub enum Value {
@@ -140,8 +141,12 @@ impl CmdRegister {
&self.deltas
}
pub(super) fn snapshot(&self) -> Option<(&Value, &[(String, Value)])> {
self.sound.as_ref().map(|s| (s, self.params.as_slice()))
pub(super) fn snapshot(&self) -> Option<CmdSnapshot<'_>> {
if self.sound.is_some() || !self.params.is_empty() {
Some((self.sound.as_ref(), self.params.as_slice()))
} else {
None
}
}
pub(super) fn clear(&mut self) {

View File

@@ -147,9 +147,12 @@ 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);
let sound = resolved_sound_val.as_str()?.to_string();
let (sound_opt, params) = cmd.snapshot().ok_or("nothing to emit")?;
let resolved_sound_val = sound_opt.map(|sv| resolve_cycling(sv, emit_idx));
let sound_str = match &resolved_sound_val {
Some(v) => Some(v.as_str()?.to_string()),
None => None,
};
let resolved_params: Vec<(String, String)> =
params.iter().map(|(k, v)| {
let resolved = resolve_cycling(v, emit_idx);
@@ -162,8 +165,8 @@ 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.into_owned()))
emit_output(sound_str.as_deref(), &resolved_params, ctx.step_duration(), delta_secs, outputs);
Ok(resolved_sound_val.map(|v| v.into_owned()))
};
while pc < ops.len() {
@@ -796,26 +799,34 @@ fn is_tempo_scaled_param(name: &str) -> bool {
}
fn emit_output(
sound: &str,
sound: Option<&str>,
params: &[(String, String)],
step_duration: f64,
nudge_secs: f64,
outputs: &mut Vec<String>,
) {
let mut pairs = vec![("sound".into(), sound.to_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()));
}
if !pairs.iter().any(|(k, _)| k == "dur") {
// 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 pair in &mut pairs {
if is_tempo_scaled_param(&pair.0) {
if let Ok(val) = pair.1.parse::<f64>() {

View File

@@ -1,6 +1,9 @@
# Welcome to Cagire
Cagire is a terminal-based step sequencer for live coding music. Each step in a pattern contains a **Forth** script that produces sound and create events. It is made by BuboBubo (Raphaël Maurice Forment): [https://raphaelforment.fr](https://raphaelforment.fr). Cagire is open-source (AGPL-3.0 licensed) and available on GitHub : [https://github.com/BuboBubo/cagire](https://github.com/BuboBubo/cagire). This help view will teach you everything you need to know to start using Cagire and and to live code with it.
Cagire is a terminal-based step sequencer for live coding music. Each step in a pattern contains a **Forth** script that produces sound and create events. It is made by BuboBubo (Raphaël Maurice Forment): [https://raphaelforment.fr](https://raphaelforment.fr). Cagire is open-source (AGPL-3.0 licensed) and available on GitHub : [https://github.com/BuboBubo/cagire](https://github.com/BuboBubo/cagire). This help view will teach you everything you need to know to start using Cagire and and to live code with it. To use Cagire, you will need to understand two things:
1) How the sequencer works: dealing with steps, patterns and banks.
2) How to write a script: how to make sound using code.
## Pages

View File

@@ -245,6 +245,7 @@ fn preprocess_underscores(md: &str) -> String {
fn parse_markdown(md: &str) -> Vec<RLine<'static>> {
let processed = preprocess_underscores(md);
eprintln!("DEBUG parse_markdown: processed={:?}", &processed[..100.min(processed.len())]);
let text = minimad::Text::from(processed.as_str());
let mut lines = Vec::new();
@@ -315,6 +316,11 @@ fn compound_to_spans(compound: Compound, base: Style, out: &mut Vec<Span<'static
let theme = theme::get();
let mut style = base;
// DEBUG: Check if bold flag is being set
if compound.bold || compound.src.contains("**") {
eprintln!("compound: bold={} src={:?}", compound.bold, compound.src);
}
if compound.bold {
style = style.add_modifier(Modifier::BOLD);
}

View File

@@ -34,7 +34,7 @@ fn auto_delaytime() {
#[test]
fn emit_no_sound() {
expect_error(".", "no sound set");
expect_error(".", "nothing to emit");
}
#[test]
@@ -87,3 +87,22 @@ fn bank_param() {
assert!(outputs[0].contains("sound/loop"));
assert!(outputs[0].contains("bank/a"));
}
#[test]
fn param_only_emit() {
let outputs = expect_outputs(r#"0 voice 880 freq ."#, 1);
assert!(outputs[0].contains("voice/0"));
assert!(outputs[0].contains("freq/880"));
assert!(!outputs[0].contains("sound/"));
assert!(!outputs[0].contains("dur/"));
assert!(!outputs[0].contains("delaytime/"));
}
#[test]
fn param_only_multiple_params() {
let outputs = expect_outputs(r#"0 voice 440 freq 0.5 gain ."#, 1);
assert!(outputs[0].contains("voice/0"));
assert!(outputs[0].contains("freq/440"));
assert!(outputs[0].contains("gain/0.5"));
assert!(!outputs[0].contains("sound/"));
}