diff --git a/crates/forth/src/words/effects.rs b/crates/forth/src/words/effects.rs index 30bce0b..5d1cc4e 100644 --- a/crates/forth/src/words/effects.rs +++ b/crates/forth/src/words/effects.rs @@ -35,7 +35,7 @@ pub(super) const WORDS: &[Word] = &[ }, Word { name: "attack", - aliases: &["att"], + aliases: &["att", "a"], category: "Envelope", stack: "(v.. --)", desc: "Set attack time", @@ -45,7 +45,7 @@ pub(super) const WORDS: &[Word] = &[ }, Word { name: "decay", - aliases: &["dec"], + aliases: &["dec", "d"], category: "Envelope", stack: "(v.. --)", desc: "Set decay time", @@ -55,7 +55,7 @@ pub(super) const WORDS: &[Word] = &[ }, Word { name: "sustain", - aliases: &["sus"], + aliases: &["sus", "s"], category: "Envelope", stack: "(v.. --)", desc: "Set sustain level", @@ -65,7 +65,7 @@ pub(super) const WORDS: &[Word] = &[ }, Word { name: "release", - aliases: &["rel"], + aliases: &["rel", "r"], category: "Envelope", stack: "(v.. --)", desc: "Set release time", diff --git a/crates/forth/src/words/sound.rs b/crates/forth/src/words/sound.rs index cbfac9e..86679a2 100644 --- a/crates/forth/src/words/sound.rs +++ b/crates/forth/src/words/sound.rs @@ -6,7 +6,7 @@ pub(super) const WORDS: &[Word] = &[ // Sound Word { name: "sound", - aliases: &["s"], + aliases: &["snd"], category: "Sound", stack: "(name --)", desc: "Begin sound command", @@ -377,6 +377,16 @@ pub(super) const WORDS: &[Word] = &[ compile: Param, varargs: true, }, + Word { + name: "partials", + aliases: &[], + category: "Oscillator", + stack: "(v.. --)", + desc: "Set number of active harmonics (add source only)", + example: "16 partials", + compile: Param, + varargs: true, + }, Word { name: "coarse", aliases: &[], diff --git a/tests/forth/definitions.rs b/tests/forth/definitions.rs index 25fa25e..397fd75 100644 --- a/tests/forth/definitions.rs +++ b/tests/forth/definitions.rs @@ -23,7 +23,7 @@ fn redefine_word_overwrites() { #[test] fn word_with_param() { - let outputs = expect_outputs(": loud 0.9 gain ; \"kick\" s loud .", 1); + let outputs = expect_outputs(": loud 0.9 gain ; \"kick\" snd loud .", 1); assert!(outputs[0].contains("gain/0.9")); } @@ -98,7 +98,7 @@ fn define_word_containing_quotation() { #[test] fn define_word_with_sound() { - let outputs = expect_outputs(": kick \"kick\" s . ; kick", 1); + let outputs = expect_outputs(": kick \"kick\" snd . ; kick", 1); assert!(outputs[0].contains("sound/kick")); } diff --git a/tests/forth/quotations.rs b/tests/forth/quotations.rs index 05a2e83..1b95900 100644 --- a/tests/forth/quotations.rs +++ b/tests/forth/quotations.rs @@ -59,20 +59,20 @@ fn nested_quotations() { #[test] fn quotation_with_param() { - let outputs = expect_outputs(r#""kick" s ( 2 distort ) 1 ? ."#, 1); + let outputs = expect_outputs(r#""kick" snd ( 2 distort ) 1 ? ."#, 1); assert!(outputs[0].contains("distort/2")); } #[test] fn quotation_skips_param() { - let outputs = expect_outputs(r#""kick" s ( 2 distort ) 0 ? ."#, 1); + let outputs = expect_outputs(r#""kick" snd ( 2 distort ) 0 ? ."#, 1); assert!(!outputs[0].contains("distort")); } #[test] fn quotation_with_emit() { // When true, . should fire - let outputs = expect_outputs(r#""kick" s ( . ) 1 ?"#, 1); + let outputs = expect_outputs(r#""kick" snd ( . ) 1 ?"#, 1); assert!(outputs[0].contains("kick")); } @@ -81,7 +81,7 @@ fn quotation_skips_emit() { // When false, . should not fire let f = forth(); let outputs = f - .evaluate(r#""kick" s ( . ) 0 ?"#, &default_ctx()) + .evaluate(r#""kick" snd ( . ) 0 ?"#, &default_ctx()) .unwrap(); // No output since . was skipped and no implicit emit assert_eq!(outputs.len(), 0); @@ -109,7 +109,7 @@ fn every_with_quotation_integration() { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); let outputs = f - .evaluate(r#""kick" s ( 2 distort ) 2 every ."#, &ctx) + .evaluate(r#""kick" snd ( 2 distort ) 2 every ."#, &ctx) .unwrap(); if iter % 2 == 0 { assert!( @@ -134,7 +134,7 @@ fn bjork_with_sound() { let ctx = ctx_with(|c| c.runs = 2); // position 2 is a hit for (3,8) let f = forth(); let outputs = f - .evaluate(r#""kick" s ( 2 distort ) 3 8 bjork ."#, &ctx) + .evaluate(r#""kick" snd ( 2 distort ) 3 8 bjork ."#, &ctx) .unwrap(); assert!(outputs[0].contains("distort/2")); } @@ -161,7 +161,7 @@ fn when_and_unless_complementary() { let f = forth(); let outputs = f .evaluate( - r#""kick" s ( 2 distort ) iter 2 mod 0 = ? ( 4 distort ) iter 2 mod 0 = !? ."#, + r#""kick" snd ( 2 distort ) iter 2 mod 0 = ? ( 4 distort ) iter 2 mod 0 = !? ."#, &ctx, ) .unwrap(); diff --git a/tests/forth/sound.rs b/tests/forth/sound.rs index b8f4ce5..fb9ed20 100644 --- a/tests/forth/sound.rs +++ b/tests/forth/sound.rs @@ -8,13 +8,13 @@ fn basic_emit() { #[test] fn alias_s() { - let outputs = expect_outputs(r#""snare" s ."#, 1); + let outputs = expect_outputs(r#""snare" snd ."#, 1); assert!(outputs[0].contains("sound/snare")); } #[test] fn with_params() { - let outputs = expect_outputs(r#""kick" s 440 freq 0.5 gain ."#, 1); + let outputs = expect_outputs(r#""kick" snd 440 freq 0.5 gain ."#, 1); assert!(outputs[0].contains("sound/kick")); assert!(outputs[0].contains("freq/440")); assert!(outputs[0].contains("gain/0.5")); @@ -22,13 +22,13 @@ fn with_params() { #[test] fn auto_dur() { - let outputs = expect_outputs(r#""kick" s ."#, 1); + let outputs = expect_outputs(r#""kick" snd ."#, 1); assert!(outputs[0].contains("dur/")); } #[test] fn auto_delaytime() { - let outputs = expect_outputs(r#""kick" s ."#, 1); + let outputs = expect_outputs(r#""kick" snd ."#, 1); assert!(outputs[0].contains("delaytime/")); } @@ -39,7 +39,7 @@ fn emit_no_sound() { #[test] fn multiple_emits() { - let outputs = expect_outputs(r#""kick" s . "snare" s ."#, 2); + let outputs = expect_outputs(r#""kick" snd . "snare" snd ."#, 2); assert!(outputs[0].contains("sound/kick")); assert!(outputs[1].contains("sound/snare")); } @@ -48,7 +48,7 @@ fn multiple_emits() { fn envelope_params() { // Values are tempo-scaled: 0.01 * step_duration(0.125) = 0.00125, etc. let outputs = expect_outputs( - r#""synth" s 0.01 attack 0.1 decay 0.7 sustain 0.3 release ."#, + r#""synth" snd 0.01 attack 0.1 decay 0.7 sustain 0.3 release ."#, 1, ); assert!(outputs[0].contains("attack/0.00125")); @@ -59,14 +59,14 @@ fn envelope_params() { #[test] fn filter_params() { - let outputs = expect_outputs(r#""synth" s 2000 lpf 0.5 lpq ."#, 1); + let outputs = expect_outputs(r#""synth" snd 2000 lpf 0.5 lpq ."#, 1); assert!(outputs[0].contains("lpf/2000")); assert!(outputs[0].contains("lpq/0.5")); } #[test] fn adsr_sets_all_envelope_params() { - let outputs = expect_outputs(r#""synth" s 0.01 0.1 0.5 0.3 adsr ."#, 1); + let outputs = expect_outputs(r#""synth" snd 0.01 0.1 0.5 0.3 adsr ."#, 1); assert!(outputs[0].contains("attack/0.00125")); assert!(outputs[0].contains("decay/0.0125")); assert!(outputs[0].contains("sustain/0.5")); @@ -75,7 +75,7 @@ fn adsr_sets_all_envelope_params() { #[test] fn ad_sets_attack_decay_sustain_zero() { - let outputs = expect_outputs(r#""synth" s 0.01 0.1 ad ."#, 1); + let outputs = expect_outputs(r#""synth" snd 0.01 0.1 ad ."#, 1); assert!(outputs[0].contains("attack/0.00125")); assert!(outputs[0].contains("decay/0.0125")); assert!(outputs[0].contains("sustain/0")); @@ -83,7 +83,7 @@ fn ad_sets_attack_decay_sustain_zero() { #[test] fn bank_param() { - let outputs = expect_outputs(r#""loop" s "a" bank ."#, 1); + let outputs = expect_outputs(r#""loop" snd "a" bank ."#, 1); assert!(outputs[0].contains("sound/loop")); assert!(outputs[0].contains("bank/a")); } @@ -109,7 +109,7 @@ fn param_only_multiple_params() { #[test] fn polyphonic_notes() { - let outputs = expect_outputs(r#"60 64 67 note sine s ."#, 3); + let outputs = expect_outputs(r#"60 64 67 note sine snd ."#, 3); assert!(outputs[0].contains("note/60")); assert!(outputs[1].contains("note/64")); assert!(outputs[2].contains("note/67")); @@ -117,14 +117,14 @@ fn polyphonic_notes() { #[test] fn polyphonic_sounds() { - let outputs = expect_outputs(r#"440 freq kick hat s ."#, 2); + let outputs = expect_outputs(r#"440 freq kick hat snd ."#, 2); assert!(outputs[0].contains("sound/kick")); assert!(outputs[1].contains("sound/hat")); } #[test] fn polyphonic_cycling() { - let outputs = expect_outputs(r#"60 64 67 note 0.5 1.0 gain sine s ."#, 3); + let outputs = expect_outputs(r#"60 64 67 note 0.5 1.0 gain sine snd ."#, 3); assert!(outputs[0].contains("note/60")); assert!(outputs[0].contains("gain/0.5")); assert!(outputs[1].contains("note/64")); @@ -135,7 +135,7 @@ fn polyphonic_cycling() { #[test] fn polyphonic_with_at() { - let outputs = expect_outputs(r#"0 0.5 at 60 64 note sine s ."#, 4); + let outputs = expect_outputs(r#"0 0.5 at 60 64 note sine snd ."#, 4); assert_eq!(outputs.len(), 4); } @@ -148,7 +148,7 @@ fn explicit_dur_zero_is_infinite() { #[test] fn all_before_sounds() { let outputs = expect_outputs( - r#"500 lpf 0.5 verb all "kick" s 60 note . "hat" s 70 note ."#, + r#"500 lpf 0.5 verb all "kick" snd 60 note . "hat" snd 70 note ."#, 2, ); assert!(outputs[0].contains("sound/kick")); @@ -162,7 +162,7 @@ fn all_before_sounds() { #[test] fn all_after_sounds() { let outputs = expect_outputs( - r#""kick" s 60 note . "hat" s 70 note . 500 lpf 0.5 verb all"#, + r#""kick" snd 60 note . "hat" snd 70 note . 500 lpf 0.5 verb all"#, 2, ); assert!(outputs[0].contains("sound/kick")); @@ -176,7 +176,7 @@ fn all_after_sounds() { #[test] fn noall_clears_global_params() { let outputs = expect_outputs( - r#"500 lpf all "kick" s 60 note . noall "hat" s 70 note ."#, + r#"500 lpf all "kick" snd 60 note . noall "hat" snd 70 note ."#, 2, ); assert!(outputs[0].contains("lpf/500")); @@ -187,7 +187,7 @@ fn noall_clears_global_params() { fn all_with_tempo_scaled_params() { // attack is tempo-scaled: 0.01 * step_duration(0.125) = 0.00125 let outputs = expect_outputs( - r#"0.01 attack all "kick" s 60 note ."#, + r#"0.01 attack all "kick" snd 60 note ."#, 1, ); assert!(outputs[0].contains("attack/0.00125")); @@ -196,7 +196,7 @@ fn all_with_tempo_scaled_params() { #[test] fn all_per_sound_override() { let outputs = expect_outputs( - r#"500 lpf all "kick" s 2000 lpf . "hat" s ."#, + r#"500 lpf all "kick" snd 2000 lpf . "hat" snd ."#, 2, ); // kick has both global lpf=500 and per-sound lpf=2000; per-sound wins (comes last) @@ -210,7 +210,7 @@ fn all_persists_across_evaluations() { let f = forth(); let ctx = default_ctx(); f.evaluate(r#"500 lpf 0.5 verb all"#, &ctx).unwrap(); - let outputs = f.evaluate(r#""kick" s 60 note ."#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd 60 note ."#, &ctx).unwrap(); assert_eq!(outputs.len(), 1); assert!(outputs[0].contains("lpf/500"), "global lpf missing: {}", outputs[0]); assert!(outputs[0].contains("verb/0.5"), "global verb missing: {}", outputs[0]); @@ -222,7 +222,7 @@ fn noall_clears_across_evaluations() { let ctx = default_ctx(); f.evaluate(r#"500 lpf all"#, &ctx).unwrap(); f.evaluate(r#"noall"#, &ctx).unwrap(); - let outputs = f.evaluate(r#""kick" s 60 note ."#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd 60 note ."#, &ctx).unwrap(); assert_eq!(outputs.len(), 1); assert!(!outputs[0].contains("lpf"), "lpf should be cleared: {}", outputs[0]); } @@ -251,20 +251,20 @@ fn all_replaces_previous_global() { let ctx = default_ctx(); f.evaluate(r#"500 lpf 0.5 verb all"#, &ctx).unwrap(); f.evaluate(r#"2000 lpf all"#, &ctx).unwrap(); - let outputs = f.evaluate(r#""kick" s ."#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd ."#, &ctx).unwrap(); assert_eq!(outputs.len(), 1); assert!(outputs[0].contains("lpf/2000"), "latest lpf should be 2000: {}", outputs[0]); } #[test] fn slice_param() { - let outputs = expect_outputs(r#""break" s 8 slice ."#, 1); + let outputs = expect_outputs(r#""break" snd 8 slice ."#, 1); assert!(outputs[0].contains("slice/8")); } #[test] fn pick_param() { - let outputs = expect_outputs(r#""break" s 8 slice 3 pick ."#, 1); + let outputs = expect_outputs(r#""break" snd 8 slice 3 pick ."#, 1); assert!(outputs[0].contains("slice/8")); assert!(outputs[0].contains("pick/3")); } diff --git a/tests/forth/temporal.rs b/tests/forth/temporal.rs index 1830099..29ae0fc 100644 --- a/tests/forth/temporal.rs +++ b/tests/forth/temporal.rs @@ -56,14 +56,14 @@ fn stepdur_baseline() { #[test] fn single_emit() { - let outputs = expect_outputs(r#""kick" s ."#, 1); + let outputs = expect_outputs(r#""kick" snd ."#, 1); let deltas = get_deltas(&outputs); assert!(approx_eq(deltas[0], 0.0), "single emit at start should have delta 0"); } #[test] fn multiple_emits_all_at_zero() { - let outputs = expect_outputs(r#""kick" s . . . ."#, 4); + let outputs = expect_outputs(r#""kick" snd . . . ."#, 4); let deltas = get_deltas(&outputs); for (i, delta) in deltas.iter().enumerate() { assert!(approx_eq(*delta, 0.0), "emit {}: expected delta 0, got {}", i, delta); @@ -72,7 +72,7 @@ fn multiple_emits_all_at_zero() { #[test] fn sound_persists() { - let outputs = expect_outputs(r#""kick" s . . "hat" s . ."#, 4); + let outputs = expect_outputs(r#""kick" snd . . "hat" snd . ."#, 4); let sounds = get_sounds(&outputs); assert_eq!(sounds[0], "kick"); assert_eq!(sounds[1], "kick"); @@ -82,14 +82,14 @@ fn sound_persists() { #[test] fn alternating_sounds() { - let outputs = expect_outputs(r#""kick" s . "snare" s . "kick" s . "snare" s ."#, 4); + let outputs = expect_outputs(r#""kick" snd . "snare" snd . "kick" snd . "snare" snd ."#, 4); let sounds = get_sounds(&outputs); assert_eq!(sounds, vec!["kick", "snare", "kick", "snare"]); } #[test] fn dur_is_step_duration() { - let outputs = expect_outputs(r#""kick" s ."#, 1); + let outputs = expect_outputs(r#""kick" snd ."#, 1); let durs = get_durs(&outputs); assert!(approx_eq(durs[0], 0.5), "dur should be 4 * step_duration (0.5), got {}", durs[0]); } @@ -99,7 +99,7 @@ fn cycle_picks_by_runs() { for runs in 0..4 { let ctx = ctx_with(|c| c.runs = runs); let f = forth(); - let outputs = f.evaluate(r#""kick" s ( . ) ( ) 2 cycle"#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd ( . ) ( ) 2 cycle"#, &ctx).unwrap(); if runs % 2 == 0 { assert_eq!(outputs.len(), 1, "runs={}: emit should be picked", runs); } else { @@ -113,7 +113,7 @@ fn pcycle_picks_by_iter() { for iter in 0..4 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); - let outputs = f.evaluate(r#""kick" s ( . ) ( ) 2 pcycle"#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd ( . ) ( ) 2 pcycle"#, &ctx).unwrap(); if iter % 2 == 0 { assert_eq!(outputs.len(), 1, "iter={}: emit should be picked", iter); } else { @@ -128,7 +128,7 @@ fn cycle_with_sounds() { let ctx = ctx_with(|c| c.runs = runs); let f = forth(); let outputs = f.evaluate( - r#"( "kick" s . ) ( "hat" s . ) ( "snare" s . ) 3 cycle"#, + r#"( "kick" snd . ) ( "hat" snd . ) ( "snare" snd . ) 3 cycle"#, &ctx ).unwrap(); assert_eq!(outputs.len(), 1, "runs={}: expected 1 output", runs); @@ -141,7 +141,7 @@ fn cycle_with_sounds() { #[test] fn at_single_delta() { - let outputs = expect_outputs(r#"0.5 at "kick" s ."#, 1); + let outputs = expect_outputs(r#"0.5 at "kick" snd ."#, 1); let deltas = get_deltas(&outputs); let step_dur = 0.125; assert!(approx_eq(deltas[0], 0.5 * step_dur), "expected delta at 0.5 of step, got {}", deltas[0]); @@ -149,7 +149,7 @@ fn at_single_delta() { #[test] fn at_list_deltas() { - let outputs = expect_outputs(r#"0 0.5 at "kick" s ."#, 2); + let outputs = expect_outputs(r#"0 0.5 at "kick" snd ."#, 2); let deltas = get_deltas(&outputs); let step_dur = 0.125; assert!(approx_eq(deltas[0], 0.0), "expected delta 0, got {}", deltas[0]); @@ -158,7 +158,7 @@ fn at_list_deltas() { #[test] fn at_three_deltas() { - let outputs = expect_outputs(r#"0 0.33 0.67 at "kick" s ."#, 3); + let outputs = expect_outputs(r#"0 0.33 0.67 at "kick" snd ."#, 3); let deltas = get_deltas(&outputs); let step_dur = 0.125; assert!(approx_eq(deltas[0], 0.0), "expected delta 0"); @@ -168,7 +168,7 @@ fn at_three_deltas() { #[test] fn at_persists_across_emits() { - let outputs = expect_outputs(r#"0 0.5 at "kick" s . "hat" s ."#, 4); + let outputs = expect_outputs(r#"0 0.5 at "kick" snd . "hat" snd ."#, 4); let sounds = get_sounds(&outputs); assert_eq!(sounds, vec!["kick", "kick", "hat", "hat"]); } @@ -176,14 +176,14 @@ fn at_persists_across_emits() { #[test] fn at_reset_with_zero() { - let outputs = expect_outputs(r#"0 0.5 at "kick" s . 0.0 at "hat" s ."#, 3); + let outputs = expect_outputs(r#"0 0.5 at "kick" snd . 0.0 at "hat" snd ."#, 3); let sounds = get_sounds(&outputs); assert_eq!(sounds, vec!["kick", "kick", "hat"]); } #[test] fn clear_resets_at_deltas() { - let outputs = expect_outputs(r#"0 0.5 at "kick" s . clear "hat" s ."#, 3); + let outputs = expect_outputs(r#"0 0.5 at "kick" snd . clear "hat" snd ."#, 3); let sounds = get_sounds(&outputs); assert_eq!(sounds, vec!["kick", "kick", "hat"]); let deltas = get_deltas(&outputs); @@ -196,7 +196,7 @@ fn at_records_selected_spans() { let f = forth(); let mut trace = ExecutionTrace::default(); - let script = r#"0 0.5 0.75 at "kick" s ."#; + let script = r#"0 0.5 0.75 at "kick" snd ."#; f.evaluate_with_trace(script, &default_ctx(), &mut trace).unwrap(); // Should have 6 selected spans: 3 for at deltas + 3 for sound (one per emit) @@ -226,7 +226,7 @@ fn get_gains(outputs: &[String]) -> Vec { #[test] fn arp_auto_subdivide() { - let outputs = expect_outputs(r#"sine s c4 e4 g4 b4 arp note ."#, 4); + let outputs = expect_outputs(r#"sine snd c4 e4 g4 b4 arp note ."#, 4); let notes = get_notes(&outputs); assert!(approx_eq(notes[0], 60.0)); assert!(approx_eq(notes[1], 64.0)); @@ -242,7 +242,7 @@ fn arp_auto_subdivide() { #[test] fn arp_with_explicit_at() { - let outputs = expect_outputs(r#"0 0.25 0.5 0.75 at sine s c4 e4 g4 b4 arp note ."#, 4); + let outputs = expect_outputs(r#"0 0.25 0.5 0.75 at sine snd c4 e4 g4 b4 arp note ."#, 4); let notes = get_notes(&outputs); assert!(approx_eq(notes[0], 60.0)); assert!(approx_eq(notes[1], 64.0)); @@ -258,14 +258,14 @@ fn arp_with_explicit_at() { #[test] fn arp_single_note() { - let outputs = expect_outputs(r#"sine s c4 arp note ."#, 1); + let outputs = expect_outputs(r#"sine snd c4 arp note ."#, 1); let notes = get_notes(&outputs); assert!(approx_eq(notes[0], 60.0)); } #[test] fn arp_fewer_deltas_than_notes() { - let outputs = expect_outputs(r#"0 0.5 at sine s c4 e4 g4 b4 arp note ."#, 4); + let outputs = expect_outputs(r#"0 0.5 at sine snd c4 e4 g4 b4 arp note ."#, 4); let notes = get_notes(&outputs); assert!(approx_eq(notes[0], 60.0)); assert!(approx_eq(notes[1], 64.0)); @@ -281,7 +281,7 @@ fn arp_fewer_deltas_than_notes() { #[test] fn arp_fewer_notes_than_deltas() { - let outputs = expect_outputs(r#"0 0.25 0.5 0.75 at sine s c4 e4 arp note ."#, 4); + let outputs = expect_outputs(r#"0 0.25 0.5 0.75 at sine snd c4 e4 arp note ."#, 4); let notes = get_notes(&outputs); assert!(approx_eq(notes[0], 60.0)); assert!(approx_eq(notes[1], 64.0)); @@ -291,7 +291,7 @@ fn arp_fewer_notes_than_deltas() { #[test] fn arp_multiple_params() { - let outputs = expect_outputs(r#"sine s c4 e4 g4 arp note 0.5 0.7 0.9 arp gain ."#, 3); + let outputs = expect_outputs(r#"sine snd c4 e4 g4 arp note 0.5 0.7 0.9 arp gain ."#, 3); let notes = get_notes(&outputs); assert!(approx_eq(notes[0], 60.0)); assert!(approx_eq(notes[1], 64.0)); @@ -305,7 +305,7 @@ fn arp_multiple_params() { #[test] fn arp_no_arp_unchanged() { // Standard CycleList without arp → cross-product (backward compat) - let outputs = expect_outputs(r#"0 0.5 at sine s c4 e4 note ."#, 4); + let outputs = expect_outputs(r#"0 0.5 at sine snd c4 e4 note ."#, 4); let notes = get_notes(&outputs); // Cross-product: each note at each delta assert!(approx_eq(notes[0], 60.0)); @@ -318,7 +318,7 @@ fn arp_no_arp_unchanged() { fn arp_mixed_cycle_and_arp() { // CycleList sound (2) + ArpList note (3) → 3 arp × 2 poly = 6 voices // Each arp step plays both sine and saw simultaneously (poly stacking) - let outputs = expect_outputs(r#"sine saw s c4 e4 g4 arp note ."#, 6); + let outputs = expect_outputs(r#"sine saw snd c4 e4 g4 arp note ."#, 6); let sounds = get_sounds(&outputs); // Arp step 0: poly 0=sine, poly 1=saw assert_eq!(sounds[0], "sine"); @@ -346,7 +346,7 @@ fn every_offset_fires_at_offset() { for iter in 0..8 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); - let outputs = f.evaluate(r#""kick" s ( . ) 4 2 every+"#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd ( . ) 4 2 every+"#, &ctx).unwrap(); if iter % 4 == 2 { assert_eq!(outputs.len(), 1, "iter={}: should fire", iter); } else { @@ -361,7 +361,7 @@ fn every_offset_wraps_large_offset() { for iter in 0..8 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); - let outputs = f.evaluate(r#""kick" s ( . ) 4 6 every+"#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd ( . ) 4 6 every+"#, &ctx).unwrap(); if iter % 4 == 2 { assert_eq!(outputs.len(), 1, "iter={}: should fire (wrapped offset)", iter); } else { @@ -375,7 +375,7 @@ fn except_offset_inverse() { for iter in 0..8 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); - let outputs = f.evaluate(r#""kick" s ( . ) 4 2 except+"#, &ctx).unwrap(); + let outputs = f.evaluate(r#""kick" snd ( . ) 4 2 except+"#, &ctx).unwrap(); if iter % 4 != 2 { assert_eq!(outputs.len(), 1, "iter={}: should fire", iter); } else { @@ -389,8 +389,8 @@ fn every_offset_zero_is_same_as_every() { for iter in 0..8 { let ctx = ctx_with(|c| c.iter = iter); let f = forth(); - let a = f.evaluate(r#""kick" s ( . ) 3 every"#, &ctx).unwrap(); - let b = f.evaluate(r#""kick" s ( . ) 3 0 every+"#, &ctx).unwrap(); + let a = f.evaluate(r#""kick" snd ( . ) 3 every"#, &ctx).unwrap(); + let b = f.evaluate(r#""kick" snd ( . ) 3 0 every+"#, &ctx).unwrap(); assert_eq!(a.len(), b.len(), "iter={}: every and every+ 0 should match", iter); } }