///|
fn expected_counter_duration(species : Species) -> Int {
match species {
First => 2
Second => 1
Third => 1
Fourth => 2
Fifth => 1
}
}
///|
fn allowed_note_count(
species : Species,
cantus_count : Int,
counter_count : Int,
) -> Bool {
match species {
First => counter_count == cantus_count
Second => counter_count == cantus_count * 2
Third => counter_count == cantus_count * 4
Fourth => counter_count == cantus_count
Fifth => counter_count >= cantus_count
}
}
///|
pub fn analyze_species_shape(
cantus : Array[Note],
counter : Array[Note],
options : AnalysisOptions,
) -> Array[Finding] {
let out : Array[Finding] = []
if !allowed_note_count(options.species, cantus.length(), counter.length()) {
out.push(
Finding::warning(
rule=SpeciesRhythm,
index=0,
message="counterpoint event count does not match selected species",
),
)
}
let expected = expected_counter_duration(options.species)
if options.species != Fifth {
for i, n in counter {
if n.duration != expected {
out.push(
Finding::warning(
rule=SpeciesRhythm,
index=i,
message="duration \{n.duration} differs from expected species duration \{expected}",
),
)
}
}
}
if options.species == Fourth {
for i, n in counter {
if i > 0 && !n.tied_from_previous {
out.push(
Finding::warning(
rule=SpeciesRhythm,
index=i,
message="fourth species expects suspension ties after the first note",
),
)
}
}
}
out
}