Feat: UI/UX and ducking compressor
This commit is contained in:
@@ -209,6 +209,37 @@ fn bounce_underflow() {
|
||||
expect_error("1 2 5 bounce", "stack underflow");
|
||||
}
|
||||
|
||||
// pbounce
|
||||
|
||||
#[test]
|
||||
fn pbounce_by_iter() {
|
||||
let expected = [60, 64, 67, 72, 67, 64, 60, 64];
|
||||
for (iter, &exp) in expected.iter().enumerate() {
|
||||
let ctx = ctx_with(|c| c.iter = iter);
|
||||
let f = run_ctx("60 64 67 72 4 pbounce", &ctx);
|
||||
assert_eq!(stack_int(&f), exp, "pbounce at iter={}", iter);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pbounce_single() {
|
||||
for iter in 0..5 {
|
||||
let ctx = ctx_with(|c| c.iter = iter);
|
||||
let f = run_ctx("42 1 pbounce", &ctx);
|
||||
assert_eq!(stack_int(&f), 42, "pbounce single at iter={}", iter);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pbounce_with_bracket() {
|
||||
let expected = [60, 64, 67, 64, 60, 64];
|
||||
for (iter, &exp) in expected.iter().enumerate() {
|
||||
let ctx = ctx_with(|c| c.iter = iter);
|
||||
let f = run_ctx("[ 60 64 67 ] pbounce", &ctx);
|
||||
assert_eq!(stack_int(&f), exp, "pbounce bracket at iter={}", iter);
|
||||
}
|
||||
}
|
||||
|
||||
// wchoose
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -338,3 +338,59 @@ fn arp_mixed_cycle_and_arp() {
|
||||
assert!(approx_eq(notes[4], 67.0));
|
||||
assert!(approx_eq(notes[5], 67.0));
|
||||
}
|
||||
|
||||
// --- every+ / except+ tests ---
|
||||
|
||||
#[test]
|
||||
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();
|
||||
if iter % 4 == 2 {
|
||||
assert_eq!(outputs.len(), 1, "iter={}: should fire", iter);
|
||||
} else {
|
||||
assert_eq!(outputs.len(), 0, "iter={}: should not fire", iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_offset_wraps_large_offset() {
|
||||
// offset 6 with n=4 → 6 % 4 = 2, same as offset 2
|
||||
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();
|
||||
if iter % 4 == 2 {
|
||||
assert_eq!(outputs.len(), 1, "iter={}: should fire (wrapped offset)", iter);
|
||||
} else {
|
||||
assert_eq!(outputs.len(), 0, "iter={}: should not fire", iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
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();
|
||||
if iter % 4 != 2 {
|
||||
assert_eq!(outputs.len(), 1, "iter={}: should fire", iter);
|
||||
} else {
|
||||
assert_eq!(outputs.len(), 0, "iter={}: should not fire", iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
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();
|
||||
assert_eq!(a.len(), b.len(), "iter={}: every and every+ 0 should match", iter);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user