diff --git a/partitura/musicanalysis/note_features.py b/partitura/musicanalysis/note_features.py index 92e07c0b..799d340f 100644 --- a/partitura/musicanalysis/note_features.py +++ b/partitura/musicanalysis/note_features.py @@ -1073,6 +1073,46 @@ def metrical_strength_feature(na, part, **kwargs): return W, names +def measure_feature(na, part, **kwargs): + """Measure feature + + This feature encodes the measure each note is in. + + """ + notes_list = part.notes_tied if not np.all(na["pitch"] == 0) else part.rests + notes = {n.id:n for n in notes_list} + bm = part.beat_map + + global_start = bm(part.first_point.t) + global_end = bm(part.last_point.t) + global_number = 0 # default global measure number + + names = [ + "measure_number", + "measure_start_beat", + "measure_end_beat", + ] + W = np.zeros((len(notes), 3)) + + for i, na_n in enumerate(na): + n = notes[na_n["id"]] + measure = next(n.start.iter_prev(score.Measure, eq=True), None) + + if measure: + start = bm(measure.start.t) + end = bm(measure.end.t) + number = measure.number + else: + start = global_start + end = global_end + number = global_number + + W[i, 0] = number + W[i, 1] = start + W[i, 2] = end + + return W, names + def time_signature_feature(na, part, **kwargs): """TIme Signature feature diff --git a/tests/data/musicxml/test_note_features.xml b/tests/data/musicxml/test_note_features.xml index 371ed54d..a206da7e 100644 --- a/tests/data/musicxml/test_note_features.xml +++ b/tests/data/musicxml/test_note_features.xml @@ -60,7 +60,7 @@ - + @@ -138,7 +138,7 @@ - + diff --git a/tests/test_note_features.py b/tests/test_note_features.py index 891a254b..86fb92bc 100644 --- a/tests/test_note_features.py +++ b/tests/test_note_features.py @@ -59,6 +59,22 @@ def test_slur_grace_art_dyn_orn(self): self.assertTrue(np.all(dyntest), "forte feature does not match") self.assertTrue(np.all(slurtest), "slur feature does not match") + def test_measure_feature(self): + for fn in MUSICXML_NOTE_FEATURES: + score = load_musicxml(fn, force_note_ids=True) + feats = [ + "measure_feature" + ] + na = compute_note_array(score[0], feature_functions=feats) + + numtest = na["measure_feature.measure_number"] == np.array([1, 1, 1, 2, 2, 2]) + starttest = na["measure_feature.measure_start_beat"] == np.array([0, 0, 0, 4, 4, 4]) + endtest = na["measure_feature.measure_end_beat"] == np.array([4, 4, 4, 8, 8, 8]) + self.assertTrue(np.all(numtest), "measure number feature does not match") + self.assertTrue(np.all(starttest), "measure start feature does not match") + self.assertTrue(np.all(endtest), "measure end feature does not match") + + if __name__ == "__main__": unittest.main()