use super::*;
const EIGHTH: i64 = 240;
const SIXTEENTH: i64 = 120;
fn note(rtick: i64, dur: i64, beams: u8) -> BeamInput {
BeamInput {
rtick,
dur,
beams,
is_rest: false,
tuplet_id: None,
}
}
fn tuplet_note(rtick: i64, dur: i64, beams: u8, id: u32) -> BeamInput {
BeamInput {
tuplet_id: Some(id),
..note(rtick, dur, beams)
}
}
fn rest(rtick: i64, dur: i64) -> BeamInput {
BeamInput {
rtick,
dur,
beams: 1,
is_rest: true,
tuplet_id: None,
}
}
fn meter(n: u32, d: u32) -> Meter {
Meter {
numerator: n,
denominator: d,
}
}
fn seg_text(segs: &[BeamSegment]) -> String {
segs.iter()
.map(|s| format!("{}:{}", s.level, s.text.as_str()))
.collect::<Vec<_>>()
.join(" ")
}
fn plan_text(notes: &[BeamInput], m: Meter) -> Vec<String> {
plan(notes, m)
.segments
.iter()
.map(|segs| seg_text(segs))
.collect()
}
#[test]
fn four_four_eighths_group_in_fours() {
let notes: Vec<_> = (0..8).map(|i| note(i * EIGHTH, EIGHTH, 1)).collect();
assert_eq!(
plan_text(¬es, meter(4, 4)),
[
"1:begin",
"1:continue",
"1:continue",
"1:end", "1:begin",
"1:continue",
"1:continue",
"1:end", ]
);
}
#[test]
fn six_eight_eighths_group_in_threes() {
let notes: Vec<_> = (0..6).map(|i| note(i * EIGHTH, EIGHTH, 1)).collect();
assert_eq!(
plan_text(¬es, meter(6, 8)),
[
"1:begin",
"1:continue",
"1:end", "1:begin",
"1:continue",
"1:end", ]
);
}
#[test]
fn two_four_eighths_group_in_twos() {
let notes: Vec<_> = (0..4).map(|i| note(i * EIGHTH, EIGHTH, 1)).collect();
assert_eq!(
plan_text(¬es, meter(2, 4)),
["1:begin", "1:end", "1:begin", "1:end"]
);
}
#[test]
fn three_four_eighths_break_every_beat() {
let notes: Vec<_> = (0..6).map(|i| note(i * EIGHTH, EIGHTH, 1)).collect();
assert_eq!(
plan_text(¬es, meter(3, 4)),
["1:begin", "1:end", "1:begin", "1:end", "1:begin", "1:end"]
);
}
#[test]
fn four_four_sixteenths_break_every_beat_with_secondary() {
let notes: Vec<_> = (0..16).map(|i| note(i * SIXTEENTH, SIXTEENTH, 2)).collect();
let plan = plan_text(¬es, meter(4, 4));
for group in 0..4 {
let base = group * 4;
assert_eq!(plan[base], "1:begin 2:begin", "group {group} note 0");
assert_eq!(plan[base + 1], "1:continue 2:continue");
assert_eq!(plan[base + 2], "1:continue 2:continue");
assert_eq!(plan[base + 3], "1:end 2:end", "group {group} note 3");
}
}
#[test]
fn secondary_beam_spans_only_the_sixteenths() {
let notes = [
note(0, EIGHTH, 1),
note(EIGHTH, SIXTEENTH, 2),
note(EIGHTH + SIXTEENTH, SIXTEENTH, 2),
note(2 * EIGHTH, EIGHTH, 1),
];
let plan = plan_text(¬es, meter(2, 2));
assert_eq!(plan[0], "1:begin");
assert_eq!(plan[1], "1:continue 2:begin");
assert_eq!(plan[2], "1:continue 2:end");
assert_eq!(plan[3], "1:end");
}
#[test]
fn dotted_eighth_sixteenth_makes_a_hook() {
let notes = [note(0, 360, 1), note(360, SIXTEENTH, 2)];
let plan = plan_text(¬es, meter(2, 4));
assert_eq!(plan[0], "1:begin");
assert_eq!(plan[1], "1:end 2:backward hook");
}
#[test]
fn triplet_eighths_beam_together() {
let notes = [
tuplet_note(0, 160, 1, 1),
tuplet_note(160, 160, 1, 1),
tuplet_note(320, 160, 1, 1),
];
assert_eq!(
plan_text(¬es, meter(4, 4)),
["1:begin", "1:continue", "1:end"]
);
}
#[test]
fn triplet_at_beat_two_starts_new_beam() {
let notes = [
note(0, EIGHTH, 1),
note(EIGHTH, EIGHTH, 1),
tuplet_note(2 * EIGHTH, 160, 1, 1),
tuplet_note(2 * EIGHTH + 160, 160, 1, 1),
tuplet_note(2 * EIGHTH + 320, 160, 1, 1),
];
assert_eq!(
plan_text(¬es, meter(2, 4)),
["1:begin", "1:end", "1:begin", "1:continue", "1:end"]
);
}
#[test]
fn rest_breaks_the_beam() {
let notes = [
note(0, EIGHTH, 1),
note(EIGHTH, EIGHTH, 1),
rest(2 * EIGHTH, EIGHTH),
note(3 * EIGHTH, EIGHTH, 1),
];
assert_eq!(plan_text(¬es, meter(2, 4)), ["1:begin", "1:end", "", ""]);
}
#[test]
fn lone_eighth_is_not_beamed() {
let notes = [note(0, EIGHTH, 1)];
assert_eq!(plan_text(¬es, meter(4, 4)), [""]);
}
#[test]
fn quarter_notes_are_never_beamed() {
let notes = [note(0, 480, 0), note(480, 480, 0)];
assert_eq!(plan_text(¬es, meter(2, 4)), ["", ""]);
}
#[test]
fn fully_beamed_triplet_hides_bracket() {
let notes = [
tuplet_note(0, 160, 1, 1),
tuplet_note(160, 160, 1, 1),
tuplet_note(320, 160, 1, 1),
];
let p = plan(¬es, meter(4, 4));
assert!(!tuplet_shows_bracket(¬es, &p.groups, 1));
}
#[test]
fn triplet_with_a_rest_shows_bracket() {
let notes = [
tuplet_note(0, 160, 1, 1),
BeamInput {
is_rest: true,
..tuplet_note(160, 160, 1, 1)
},
tuplet_note(320, 160, 1, 1),
];
let p = plan(¬es, meter(4, 4));
assert!(tuplet_shows_bracket(¬es, &p.groups, 1));
}
#[test]
fn quarter_note_triplet_shows_bracket() {
let notes = [
tuplet_note(0, 320, 0, 1),
tuplet_note(320, 320, 0, 1),
tuplet_note(640, 320, 0, 1),
];
let p = plan(¬es, meter(4, 4));
assert!(tuplet_shows_bracket(¬es, &p.groups, 1));
}
#[test]
fn exotic_meter_falls_back_to_per_beat_breaks() {
let notes: Vec<_> = (0..4).map(|i| note(i * EIGHTH, EIGHTH, 1)).collect();
assert_eq!(
plan_text(¬es, meter(7, 4)),
["1:begin", "1:end", "1:begin", "1:end"]
);
}