From 86c5a6bb795440f05d3e7cb094a5b4e09debddd8 Mon Sep 17 00:00:00 2001 From: Johannes Hentschel Date: Wed, 20 Dec 2023 16:30:53 +0100 Subject: [PATCH] fix: fixes bug when creating an excerpt containing an incomplete volta --- src/ms3/bs4_measures.py | 30 ++++++++++++++++++++------ tests/test_metarepo_files/debugging.py | 18 ++++++++++++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/ms3/bs4_measures.py b/src/ms3/bs4_measures.py index 04ce18af..fd79b7c1 100644 --- a/src/ms3/bs4_measures.py +++ b/src/ms3/bs4_measures.py @@ -831,6 +831,16 @@ def check_measure_numbers( dont_count="dont_count", numbering_offset="numbering_offset", ): + """Checks if ms3's conventions for counting measure-like units are respected by the score and warns about + discrepancies. Conventions can be satisfied either by using "Exclude from bar count" or by setting values for + "Add to bar number". + + * anacrusis has MN 0; otherwise first measure as MN 1 + * Subsequent measures with irregular length shorter than the TimeSig's nominal length should add up and only + the first increases the measure number, the other don't so that they have the same number + * the measure of each alternative ending (volta) need to start with the same measure number + """ + def ordinal(i): if i == 1: return "1st" @@ -843,15 +853,21 @@ def ordinal(i): mc2mn = dict(self.ml[[mc_col, mn_col]].itertuples(index=False)) # Check measure numbers in voltas for volta_group in self.volta_structure.values(): - for i, t in enumerate(zip(*volta_group.values()), start=1): - m = t[0] - mn = mc2mn[m] - for j, mc in enumerate(t[1:], start=2): - current_mn = mc2mn[mc] + for volta_count, volta_mcs in enumerate( + zip(*volta_group.values()), start=1 + ): + m = volta_mcs[0] + if not (mn := mc2mn.get(m)): + # this may arise when we are dealing with an excerpt where the volta has been removed + continue + for mc_count, mc in enumerate(volta_mcs[1:], start=2): + if not (current_mn := mc2mn.get(mc)): + # this may arise when we are dealing with an excerpt where the volta is only partially included + continue if current_mn != mn: self.logger.warning( - f"MC {mc}, the {ordinal(i)} measure of a {ordinal(j)} volta, should have MN {mn}, " - f"not MN {current_mn}.", + f"MC {mc}, the {ordinal(volta_count)} measure of a {ordinal(mc_count)} volta, should have " + f"MN {mn}, not MN {current_mn}.", extra={"message_id": (2, mc)}, ) diff --git a/tests/test_metarepo_files/debugging.py b/tests/test_metarepo_files/debugging.py index a14724f3..2e089fd8 100644 --- a/tests/test_metarepo_files/debugging.py +++ b/tests/test_metarepo_files/debugging.py @@ -8,7 +8,7 @@ """ import os.path -from ms3 import Parse +from ms3 import Parse, Score from ms3.logger import get_logger from ms3.operations import transform_to_resources @@ -64,5 +64,19 @@ def transform_cmd(): ) +def single_score(): + path = "~/distant_listening_corpus/ABC/MS3/n13op130_06.mscx" + return Score(path) + + if __name__ == "__main__": - extraction() + score = single_score() + score.mscx.store_excerpt( + start_mc=62, + start_mc_onset=0, + end_mc=102, + end_mc_onset=0, + exclude_end=True, + directory="/home/laser/Documents/phd/phrase_excerpts/231220_distant_listening_corpus", + suffix="_phrase776", + )