From 66f2188f28fb2c3dbed02f7bdfe70372de5e34c3 Mon Sep 17 00:00:00 2001 From: sildater Date: Fri, 6 Oct 2023 17:27:41 +0200 Subject: [PATCH 01/20] remove unnecessary escape character --- partitura/io/importnakamura.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partitura/io/importnakamura.py b/partitura/io/importnakamura.py index 22e82c34..7fb66edd 100644 --- a/partitura/io/importnakamura.py +++ b/partitura/io/importnakamura.py @@ -149,7 +149,7 @@ def load_nakamuramatch(filename: PathLike) -> Tuple[Union[np.ndarray, list]]: missing = np.fromregex(filename, pattern, dtype=dtype_missing) midi_pitch = np.array( - [note_name_to_midi_pitch(n.replace("#", r"\#")) for n in result["alignSitch"]] + [note_name_to_midi_pitch(n) for n in result["alignSitch"]] ) align_valid = result["alignID"] != "*" From 956812db0bcbe376e7dcff5806be049b423b5b14 Mon Sep 17 00:00:00 2001 From: neosatrapahereje Date: Fri, 6 Oct 2023 15:58:45 +0000 Subject: [PATCH 02/20] Format code with black (bot) --- partitura/io/importnakamura.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/partitura/io/importnakamura.py b/partitura/io/importnakamura.py index 7fb66edd..f2f32672 100644 --- a/partitura/io/importnakamura.py +++ b/partitura/io/importnakamura.py @@ -148,9 +148,7 @@ def load_nakamuramatch(filename: PathLike) -> Tuple[Union[np.ndarray, list]]: # load missing notes missing = np.fromregex(filename, pattern, dtype=dtype_missing) - midi_pitch = np.array( - [note_name_to_midi_pitch(n) for n in result["alignSitch"]] - ) + midi_pitch = np.array([note_name_to_midi_pitch(n) for n in result["alignSitch"]]) align_valid = result["alignID"] != "*" n_align = sum(align_valid) From 36848ac34b6894dcb546fa8a9e7eb46ac07f6f2c Mon Sep 17 00:00:00 2001 From: sildater Date: Wed, 11 Oct 2023 08:15:41 +0200 Subject: [PATCH 03/20] handle none type doc order --- partitura/io/exportmatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partitura/io/exportmatch.py b/partitura/io/exportmatch.py index 03e8ed3d..17105a18 100644 --- a/partitura/io/exportmatch.py +++ b/partitura/io/exportmatch.py @@ -358,7 +358,7 @@ def matchfile_from_alignment( offset_in_beats=offset_beats, score_attributes_list=score_attributes_list, ) - snote_sort_info[snote.id] = (onset_beats, snote.doc_order) + snote_sort_info[snote.id] = (onset_beats, snote.doc_order if snote.doc_order is not None else 0) # # NOTE time position is hardcoded, not pretty... Assumes there is only one tempo indication at the beginning of the score if tempo_indication is not None: From fae5d1c2b5dd4f3ee712fd1c863414c2b454b14e Mon Sep 17 00:00:00 2001 From: huispaty Date: Fri, 20 Oct 2023 19:06:17 +0200 Subject: [PATCH 04/20] voice parsing from matchfile fix --- partitura/io/importmatch.py | 20 +++++++------------- partitura/io/matchfile_utils.py | 2 +- tests/test_match_import.py | 6 +++++- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 696b9331..94507c21 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -629,15 +629,14 @@ def part_from_matchfile( except (TypeError, ValueError): # no staff attribute, or staff attribute does not end with a number note_attributes["staff"] = None - if "s" in note.ScoreAttributesList: note_attributes["voice"] = 1 else: note_attributes["voice"] = next( - (int(a) for a in note.ScoreAttributesList if number_pattern.match(a)), + (int(a[1:]) for a in note.ScoreAttributesList if number_pattern.match(a)), None, ) - + # get rid of this if as soon as we have a way to iterate over the # duration components. For now we have to treat the cases simple # and compound durations separately. @@ -692,12 +691,9 @@ def part_from_matchfile( else: part_note = score.Note(**note_attributes) - part.add(part_note, onset_divs, offset_divs) - # Check if the note is tied and if so, add the tie information if is_tied: - found = False # iterate over all notes in the Timeline that end at the starting point. for el in part_note.start.iter_ending(score.Note): if isinstance(el, score.Note): @@ -717,7 +713,6 @@ def part_from_matchfile( part_note.id ) ) - # add time signatures for ts_beat_time, ts_bar, tsg in ts: ts_beats = tsg.numerator @@ -729,7 +724,6 @@ def part_from_matchfile( else: bar_start_divs = 0 part.add(score.TimeSignature(ts_beats, ts_beat_type), bar_start_divs) - # add key signatures for ks_beat_time, ks_bar, keys in mf.key_signatures: if ks_bar in bar_times.keys(): @@ -754,11 +748,11 @@ def part_from_matchfile( score.add_measures(part) score.tie_notes(part) score.find_tuplets(part) - - if not all([n.voice for n in part.notes_tied]): - for note in part.notes_tied: - if note.voice is None: - note.voice = 1 + + # if not all([n.voice for n in part.notes_tied]): + # for note in part.notes_tied: + # if note.voice is None: + # note.voice = 1 return part diff --git a/partitura/io/matchfile_utils.py b/partitura/io/matchfile_utils.py index e6b48dc4..8cb2a639 100644 --- a/partitura/io/matchfile_utils.py +++ b/partitura/io/matchfile_utils.py @@ -45,7 +45,7 @@ pitch_class_pattern = re.compile("(?P[A-Ga-g])(?P[#bn]*)") -number_pattern = re.compile(r"\d+") +number_pattern = re.compile(r"v\d+") # For matchfiles before 1.0.0. old_version_pattern = re.compile(r"^(?P[0-9]+)\.(?P[0-9]+)") diff --git a/tests/test_match_import.py b/tests/test_match_import.py index 64356a8a..10369d3f 100644 --- a/tests/test_match_import.py +++ b/tests/test_match_import.py @@ -107,6 +107,7 @@ def test_load_match(self): ) sna_musicxml = score_musicxml.note_array() + assert np.all(sna_match['voice'] == sna_musicxml['voice']) for note in alignment: @@ -564,7 +565,7 @@ def test_stimeptime_lines(self): self.assertTrue(True) def test_snote_lines(self): - + snote_lines = [ "snote(n1,[B,n],3,0:2,1/8,1/8,-0.5000,0.0000,[v1])", "snote(n3,[G,#],3,1:1,0,1/16,0.0000,0.2500,[v3])", @@ -1992,3 +1993,6 @@ def test_match_key_signature(self): for component in ks.other_components: key_name = fifths_mode_to_key_name(component.fifths, component.mode) self.assertTrue(str(component).startswith(key_name)) + +if __name__ == "__main__": + unittest.main() From 8f8fd7cef14258c019ce6b4e6b3f968b932fc7be Mon Sep 17 00:00:00 2001 From: huispaty Date: Mon, 23 Oct 2023 14:03:08 +0200 Subject: [PATCH 05/20] voice parsing for multiple mf versions --- partitura/io/importmatch.py | 17 ++++++++++++----- partitura/io/matchfile_utils.py | 3 ++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 94507c21..53cb2c76 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -67,6 +67,7 @@ from partitura.io.matchfile_utils import ( Version, number_pattern, + vnumber_pattern, MatchTimeSignature, MatchKeySignature, format_pnote_id, @@ -631,9 +632,14 @@ def part_from_matchfile( note_attributes["staff"] = None if "s" in note.ScoreAttributesList: note_attributes["voice"] = 1 + elif any(a.startswith("v") for a in note.ScoreAttributesList): + note_attributes["voice"] = next( + (int(a[1:]) for a in note.ScoreAttributesList if vnumber_pattern.match(a)), + None, + ) else: note_attributes["voice"] = next( - (int(a[1:]) for a in note.ScoreAttributesList if number_pattern.match(a)), + (int(a) for a in note.ScoreAttributesList if number_pattern.match(a)), None, ) @@ -749,10 +755,11 @@ def part_from_matchfile( score.tie_notes(part) score.find_tuplets(part) - # if not all([n.voice for n in part.notes_tied]): - # for note in part.notes_tied: - # if note.voice is None: - # note.voice = 1 + n_voices = set([n.voice for n in part.notes]) + if len(n_voices) == 1 and None in n_voices: + for note in part.notes_tied: + if note.voice is None: + note.voice = 1 return part diff --git a/partitura/io/matchfile_utils.py b/partitura/io/matchfile_utils.py index 8cb2a639..9fe76745 100644 --- a/partitura/io/matchfile_utils.py +++ b/partitura/io/matchfile_utils.py @@ -45,7 +45,8 @@ pitch_class_pattern = re.compile("(?P[A-Ga-g])(?P[#bn]*)") -number_pattern = re.compile(r"v\d+") +number_pattern = re.compile(r"\d+") +vnumber_pattern = re.compile(r"v\d+") # For matchfiles before 1.0.0. old_version_pattern = re.compile(r"^(?P[0-9]+)\.(?P[0-9]+)") From 3b26ee75aaf175cf2077c0b7b322b91e0db2d848 Mon Sep 17 00:00:00 2001 From: sildater Date: Mon, 23 Oct 2023 16:39:45 +0200 Subject: [PATCH 06/20] check patterns --- partitura/io/matchfile_base.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/partitura/io/matchfile_base.py b/partitura/io/matchfile_base.py index d89aa2d2..67874058 100644 --- a/partitura/io/matchfile_base.py +++ b/partitura/io/matchfile_base.py @@ -799,7 +799,20 @@ def prepare_kwargs_from_matchline( matchline: str, snote_class: BaseSnoteLine, version: Version, - ) -> Dict: + pos: int = 0, + ) -> Dict: + # this is very roundabout, but since this is a class method + # we can't use instance properties without a dummy instance of cls. + # and the note class pattern is only set when instantiated + # for some note classes (expecially v0) + dummy_snote = snote_class(version = version,anchor= 0,note_name="C", + modifier="",octave=4,measure=0,beat=0, + offset=FractionalSymbolicDuration(1),duration=FractionalSymbolicDuration(1), + onset_in_beats=0.0,offset_in_beats=0.0,score_attributes_list=[]) + dummy_instance = cls(version = version, snote= dummy_snote) + match_pattern = dummy_instance.pattern.search(matchline, pos=pos) + if match_pattern is None: + raise MatchError("") snote = snote_class.from_matchline(matchline, version=version) kwargs = dict( @@ -841,7 +854,24 @@ def prepare_kwargs_from_matchline( matchline: str, note_class: BaseNoteLine, version: Version, + pos: int = 0, ) -> Dict: + # this is very roundabout, but since this is a class method + # we can't use instance properties without a dummy instance of cls. + # and the note class pattern is only set when instantiated + # for some note classes (expecially v0) + if version >= Version(1, 0, 0): + dummy_note = note_class(version = version, id="id",midi_pitch=60, + onset=0,offset=0,velocity=0,channel=0,track=0) + else: + dummy_note = note_class(version = version, id="id", note_name="C", + modifier=0, octave= 0, onset= 0, offset= 0, velocity= 0) + + dummy_instance = cls(version = version, note= dummy_note) + match_pattern = dummy_instance.pattern.search(matchline, pos=pos) + if match_pattern is None: + raise MatchError("") + note = note_class.from_matchline(matchline, version=version) kwargs = dict( From 9526dbd4a7d005c151af386ce86b7c3c29a5a6b5 Mon Sep 17 00:00:00 2001 From: neosatrapahereje Date: Mon, 23 Oct 2023 15:04:20 +0000 Subject: [PATCH 07/20] Format code with black (bot) --- partitura/io/exportmatch.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/partitura/io/exportmatch.py b/partitura/io/exportmatch.py index 17105a18..3311c75b 100644 --- a/partitura/io/exportmatch.py +++ b/partitura/io/exportmatch.py @@ -358,7 +358,10 @@ def matchfile_from_alignment( offset_in_beats=offset_beats, score_attributes_list=score_attributes_list, ) - snote_sort_info[snote.id] = (onset_beats, snote.doc_order if snote.doc_order is not None else 0) + snote_sort_info[snote.id] = ( + onset_beats, + snote.doc_order if snote.doc_order is not None else 0, + ) # # NOTE time position is hardcoded, not pretty... Assumes there is only one tempo indication at the beginning of the score if tempo_indication is not None: From e117f7a37d9aa74b0818e1cab65fa791a83658db Mon Sep 17 00:00:00 2001 From: huispaty Date: Mon, 23 Oct 2023 17:26:50 +0200 Subject: [PATCH 08/20] correction for mistakenly deleted line --- partitura/io/importmatch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 53cb2c76..587356b0 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -700,6 +700,7 @@ def part_from_matchfile( part.add(part_note, onset_divs, offset_divs) # Check if the note is tied and if so, add the tie information if is_tied: + found = False # iterate over all notes in the Timeline that end at the starting point. for el in part_note.start.iter_ending(score.Note): if isinstance(el, score.Note): From 39d9741f5c01f9b9c7aae7e81b1b39306c715714 Mon Sep 17 00:00:00 2001 From: huispaty Date: Mon, 23 Oct 2023 17:40:51 +0200 Subject: [PATCH 09/20] voice info addendum on part directly --- partitura/io/importmatch.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 587356b0..3e49608b 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -743,7 +743,7 @@ def part_from_matchfile( # * use key estimation if there are multiple defined keys # fifths, mode = key_name_to_fifths_mode(key_name) part.add(score.KeySignature(keys.fifths, keys.mode), ks_bar) - + add_staffs(part) # add_clefs(part) @@ -761,6 +761,11 @@ def part_from_matchfile( for note in part.notes_tied: if note.voice is None: note.voice = 1 + elif len(n_voices) > 1 and None in n_voices: + n_voices.remove(None) + for note in part.notes_tied: + if note.voice is None: + note.voice = max(n_voices) return part From 9ba381362fed6337f775bce3aa4e184d5ad88bc2 Mon Sep 17 00:00:00 2001 From: sildater Date: Mon, 23 Oct 2023 17:48:23 +0200 Subject: [PATCH 10/20] add one to voice --- partitura/io/importmatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 3e49608b..e422d7a6 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -765,7 +765,7 @@ def part_from_matchfile( n_voices.remove(None) for note in part.notes_tied: if note.voice is None: - note.voice = max(n_voices) + note.voice = max(n_voices) + 1 return part From 465f28fa5cd54261d967e377d3f2b10544463f8a Mon Sep 17 00:00:00 2001 From: sildater Date: Mon, 23 Oct 2023 15:50:19 +0000 Subject: [PATCH 11/20] Format code with black (bot) --- partitura/io/importmatch.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index e422d7a6..618b892c 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -634,7 +634,11 @@ def part_from_matchfile( note_attributes["voice"] = 1 elif any(a.startswith("v") for a in note.ScoreAttributesList): note_attributes["voice"] = next( - (int(a[1:]) for a in note.ScoreAttributesList if vnumber_pattern.match(a)), + ( + int(a[1:]) + for a in note.ScoreAttributesList + if vnumber_pattern.match(a) + ), None, ) else: @@ -642,7 +646,7 @@ def part_from_matchfile( (int(a) for a in note.ScoreAttributesList if number_pattern.match(a)), None, ) - + # get rid of this if as soon as we have a way to iterate over the # duration components. For now we have to treat the cases simple # and compound durations separately. @@ -743,7 +747,7 @@ def part_from_matchfile( # * use key estimation if there are multiple defined keys # fifths, mode = key_name_to_fifths_mode(key_name) part.add(score.KeySignature(keys.fifths, keys.mode), ks_bar) - + add_staffs(part) # add_clefs(part) @@ -755,7 +759,7 @@ def part_from_matchfile( score.add_measures(part) score.tie_notes(part) score.find_tuplets(part) - + n_voices = set([n.voice for n in part.notes]) if len(n_voices) == 1 and None in n_voices: for note in part.notes_tied: From 54d0a6a752408120f450bcc0ffd61a1a403cff2d Mon Sep 17 00:00:00 2001 From: neosatrapahereje Date: Tue, 24 Oct 2023 08:29:41 +0000 Subject: [PATCH 12/20] Format code with black (bot) --- partitura/io/matchfile_base.py | 50 ++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/partitura/io/matchfile_base.py b/partitura/io/matchfile_base.py index 67874058..ac0b306f 100644 --- a/partitura/io/matchfile_base.py +++ b/partitura/io/matchfile_base.py @@ -800,16 +800,26 @@ def prepare_kwargs_from_matchline( snote_class: BaseSnoteLine, version: Version, pos: int = 0, - ) -> Dict: + ) -> Dict: # this is very roundabout, but since this is a class method # we can't use instance properties without a dummy instance of cls. # and the note class pattern is only set when instantiated # for some note classes (expecially v0) - dummy_snote = snote_class(version = version,anchor= 0,note_name="C", - modifier="",octave=4,measure=0,beat=0, - offset=FractionalSymbolicDuration(1),duration=FractionalSymbolicDuration(1), - onset_in_beats=0.0,offset_in_beats=0.0,score_attributes_list=[]) - dummy_instance = cls(version = version, snote= dummy_snote) + dummy_snote = snote_class( + version=version, + anchor=0, + note_name="C", + modifier="", + octave=4, + measure=0, + beat=0, + offset=FractionalSymbolicDuration(1), + duration=FractionalSymbolicDuration(1), + onset_in_beats=0.0, + offset_in_beats=0.0, + score_attributes_list=[], + ) + dummy_instance = cls(version=version, snote=dummy_snote) match_pattern = dummy_instance.pattern.search(matchline, pos=pos) if match_pattern is None: raise MatchError("") @@ -861,13 +871,29 @@ def prepare_kwargs_from_matchline( # and the note class pattern is only set when instantiated # for some note classes (expecially v0) if version >= Version(1, 0, 0): - dummy_note = note_class(version = version, id="id",midi_pitch=60, - onset=0,offset=0,velocity=0,channel=0,track=0) + dummy_note = note_class( + version=version, + id="id", + midi_pitch=60, + onset=0, + offset=0, + velocity=0, + channel=0, + track=0, + ) else: - dummy_note = note_class(version = version, id="id", note_name="C", - modifier=0, octave= 0, onset= 0, offset= 0, velocity= 0) - - dummy_instance = cls(version = version, note= dummy_note) + dummy_note = note_class( + version=version, + id="id", + note_name="C", + modifier=0, + octave=0, + onset=0, + offset=0, + velocity=0, + ) + + dummy_instance = cls(version=version, note=dummy_note) match_pattern = dummy_instance.pattern.search(matchline, pos=pos) if match_pattern is None: raise MatchError("") From a6220b57660131a6f78a4f78983b7f85170e865a Mon Sep 17 00:00:00 2001 From: sildater Date: Tue, 24 Oct 2023 11:17:30 +0200 Subject: [PATCH 13/20] solution without dummies --- partitura/io/importmatch.py | 4 ++- partitura/io/matchfile_base.py | 55 +++++----------------------------- partitura/io/matchlines_v0.py | 4 +++ 3 files changed, 14 insertions(+), 49 deletions(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 618b892c..9948ea7e 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -210,7 +210,9 @@ def load_matchfile( parse_matchline, version=version, from_matchline_methods=from_matchline_methods ) f_vec = np.vectorize(f) - parsed_lines = f_vec(np_lines).tolist() + parsed_lines_raw = f_vec(np_lines).tolist() + # do not return unparseable lines + parsed_lines = [line for line in parsed_lines_raw if line is not None] # Create MatchFile instance mf = MatchFile(lines=parsed_lines) # Validate match for duplicate snote_ids or pnote_ids diff --git a/partitura/io/matchfile_base.py b/partitura/io/matchfile_base.py index ac0b306f..0c49e77c 100644 --- a/partitura/io/matchfile_base.py +++ b/partitura/io/matchfile_base.py @@ -770,6 +770,7 @@ def prepare_kwargs_from_matchline( class BaseDeletionLine(MatchLine): out_pattern = "{SnoteLine}-deletion." + identifier_pattern = re.compile(r"-deletion\.") def __init__(self, version: Version, snote: BaseSnoteLine) -> None: super().__init__(version) @@ -801,26 +802,9 @@ def prepare_kwargs_from_matchline( version: Version, pos: int = 0, ) -> Dict: - # this is very roundabout, but since this is a class method - # we can't use instance properties without a dummy instance of cls. - # and the note class pattern is only set when instantiated - # for some note classes (expecially v0) - dummy_snote = snote_class( - version=version, - anchor=0, - note_name="C", - modifier="", - octave=4, - measure=0, - beat=0, - offset=FractionalSymbolicDuration(1), - duration=FractionalSymbolicDuration(1), - onset_in_beats=0.0, - offset_in_beats=0.0, - score_attributes_list=[], - ) - dummy_instance = cls(version=version, snote=dummy_snote) - match_pattern = dummy_instance.pattern.search(matchline, pos=pos) + + match_pattern = cls.identifier_pattern.search(matchline, pos=pos) + if match_pattern is None: raise MatchError("") snote = snote_class.from_matchline(matchline, version=version) @@ -835,6 +819,7 @@ def prepare_kwargs_from_matchline( class BaseInsertionLine(MatchLine): out_pattern = "insertion-{NoteLine}" + identifier_pattern = re.compile(r"insertion-") def __init__(self, version: Version, note: BaseNoteLine) -> None: super().__init__(version) @@ -866,35 +851,9 @@ def prepare_kwargs_from_matchline( version: Version, pos: int = 0, ) -> Dict: - # this is very roundabout, but since this is a class method - # we can't use instance properties without a dummy instance of cls. - # and the note class pattern is only set when instantiated - # for some note classes (expecially v0) - if version >= Version(1, 0, 0): - dummy_note = note_class( - version=version, - id="id", - midi_pitch=60, - onset=0, - offset=0, - velocity=0, - channel=0, - track=0, - ) - else: - dummy_note = note_class( - version=version, - id="id", - note_name="C", - modifier=0, - octave=0, - onset=0, - offset=0, - velocity=0, - ) - dummy_instance = cls(version=version, note=dummy_note) - match_pattern = dummy_instance.pattern.search(matchline, pos=pos) + match_pattern = cls.identifier_pattern.search(matchline, pos=pos) + if match_pattern is None: raise MatchError("") diff --git a/partitura/io/matchlines_v0.py b/partitura/io/matchlines_v0.py index 0fbef690..b1a7b80f 100644 --- a/partitura/io/matchlines_v0.py +++ b/partitura/io/matchlines_v0.py @@ -805,6 +805,7 @@ def from_matchline( class MatchSnoteTrailingScore(MatchSnoteDeletion): out_pattern = "{SnoteLine}-trailing_score_note." + identifier_pattern = re.compile(r"-trailing_score_note\.") def __init__(self, version: Version, snote: MatchSnote) -> None: super().__init__(version=version, snote=snote) @@ -815,6 +816,7 @@ def __init__(self, version: Version, snote: MatchSnote) -> None: class MatchSnoteNoPlayedNote(MatchSnoteDeletion): out_pattern = "{SnoteLine}-no_played_note." + identifier_pattern = re.compile(r"-no_played_note\.") def __init__(self, version: Version, snote: MatchSnote) -> None: super().__init__(version=version, snote=snote) @@ -848,6 +850,7 @@ def from_matchline( class MatchHammerBounceNote(MatchInsertionNote): out_pattern = "hammer_bounce-{NoteLine}" + identifier_pattern = re.compile(r"hammer_bounce-") def __init__(self, version: Version, note: MatchNote) -> None: super().__init__(version=version, note=note) @@ -856,6 +859,7 @@ def __init__(self, version: Version, note: MatchNote) -> None: class MatchTrailingPlayedNote(MatchInsertionNote): out_pattern = "trailing_played_note-{NoteLine}" + identifier_pattern = re.compile(r"trailing_played_note-") def __init__(self, version: Version, note: MatchNote) -> None: super().__init__(version=version, note=note) From ded12a6e97fddc9aaa10bd6e782dd78ab8c53d8c Mon Sep 17 00:00:00 2001 From: neosatrapahereje Date: Tue, 24 Oct 2023 09:52:21 +0000 Subject: [PATCH 14/20] Format code with black (bot) --- partitura/io/matchfile_base.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/partitura/io/matchfile_base.py b/partitura/io/matchfile_base.py index 0c49e77c..f0f0fca9 100644 --- a/partitura/io/matchfile_base.py +++ b/partitura/io/matchfile_base.py @@ -802,7 +802,6 @@ def prepare_kwargs_from_matchline( version: Version, pos: int = 0, ) -> Dict: - match_pattern = cls.identifier_pattern.search(matchline, pos=pos) if match_pattern is None: @@ -851,7 +850,6 @@ def prepare_kwargs_from_matchline( version: Version, pos: int = 0, ) -> Dict: - match_pattern = cls.identifier_pattern.search(matchline, pos=pos) if match_pattern is None: From 487a04877faab5dfb5368bf6263acc06ef638c49 Mon Sep 17 00:00:00 2001 From: Francesco Foscarin Date: Tue, 24 Oct 2023 17:55:27 +0200 Subject: [PATCH 15/20] Update CHANGES.md --- CHANGES.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 7ce264e5..d6fe20eb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,16 @@ Release Notes ============= +Version 1.4.1 (Released on 2023-10-25) +-------------------------------------- + +## Bug Fixes +- remove unnecessary escape characters for correct parsing of sharp accidentals in Nakamura match files. +- don't consider the propriety `doc_order` for sorting notes in the `matchfile_from_alignment` function if it is not present. This propriety is only present in parts from musicxml scores and previously resulted in an exception for other score formats. This solves https://github.com/CPJKU/partitura/issues/326 +- during matchfile parsing, voice info is now parsed as follows: If there is no voice info, all notes get assigned voice number 1. If there is only voice info for the solo voice, the non-solo voiced notes get voice 2. If multiple notes have different voices, but not every note has a voice annotated, those with voice annotation get the annotated voice number and those without voice annotation get assigned the max voice+1 voice number. Previously all notes were assigned to voice 1 if there were any None voiced note +- during matchfile parsing, all note classes are now matched correctly. Previously classes `MatchSnoteTrailingScore` and `MatchSnoteNoPlayedNote` were always marked as `MatchSnoteDeletion` and `MatchHammerBounceNote`, `MatchTrailingPlayedNote`, `MatchTrillNote` always ended up as `MatchInsertionNote`. This solves https://github.com/CPJKU/partitura/issues/286 +- during matchfile parsing, lines which can't be parsed are removed. Before they ended up as `None` in the output. + Version 1.4.0 (Released on 2023-09-22) -------------------------------------- From 45b5c795a055e2c2a838f2c153ea61388f2055bd Mon Sep 17 00:00:00 2001 From: Francesco Foscarin Date: Tue, 24 Oct 2023 17:56:04 +0200 Subject: [PATCH 16/20] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 10c3bbf2..4f9432f0 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ EMAIL = "partitura-users@googlegroups.com" AUTHOR = "Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier, Patricia Hu" REQUIRES_PYTHON = ">=3.7" -VERSION = "1.4.0" +VERSION = "1.4.1" # What packages are required for this module to be executed? REQUIRED = ["numpy", "scipy", "lxml", "lark-parser", "xmlschema", "mido"] From 826c55cbaee2a009c073ed3598ded3efb043c031 Mon Sep 17 00:00:00 2001 From: Francesco Foscarin Date: Tue, 24 Oct 2023 17:56:59 +0200 Subject: [PATCH 17/20] Update conf.py --- docs/source/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f71055dd..0b4820cc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -29,9 +29,9 @@ # built documents. # # The short X.Y version. -version = "1.4.0" # pkg_resources.get_distribution("partitura").version +version = "1.4.1" # pkg_resources.get_distribution("partitura").version # The full version, including alpha/beta/rc tags. -release = "1.4.0" +release = "1.4.1" # # The full version, including alpha/beta/rc tags # release = pkg_resources.get_distribution("partitura").version From d6786e86785d950eece8b6fb57b689c552518965 Mon Sep 17 00:00:00 2001 From: sildater Date: Wed, 25 Oct 2023 08:03:04 +0200 Subject: [PATCH 18/20] vectorization of None mask --- partitura/io/importmatch.py | 5 +++-- tests/data/match/mozart_k265_var1.match | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 9948ea7e..356dbd78 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -210,9 +210,9 @@ def load_matchfile( parse_matchline, version=version, from_matchline_methods=from_matchline_methods ) f_vec = np.vectorize(f) - parsed_lines_raw = f_vec(np_lines).tolist() + parsed_lines_raw = f_vec(np_lines) # do not return unparseable lines - parsed_lines = [line for line in parsed_lines_raw if line is not None] + parsed_lines = parsed_lines_raw[parsed_lines_raw != None].tolist() # Create MatchFile instance mf = MatchFile(lines=parsed_lines) # Validate match for duplicate snote_ids or pnote_ids @@ -632,6 +632,7 @@ def part_from_matchfile( except (TypeError, ValueError): # no staff attribute, or staff attribute does not end with a number note_attributes["staff"] = None + if "s" in note.ScoreAttributesList: note_attributes["voice"] = 1 elif any(a.startswith("v") for a in note.ScoreAttributesList): diff --git a/tests/data/match/mozart_k265_var1.match b/tests/data/match/mozart_k265_var1.match index 9c29507e..58d2686b 100644 --- a/tests/data/match/mozart_k265_var1.match +++ b/tests/data/match/mozart_k265_var1.match @@ -18,6 +18,7 @@ snote(n10,[C,n],4,1:2,0,1/4,1.0000,2.0000,[v5,staff2])-note(n5,60,1122,1160,67,1 snote(n6,[C,n],5,1:2,1/16,1/16,1.2500,1.5000,[v1,staff1])-note(n7,72,1233,1284,44,1,1). snote(n7,[B,n],4,1:2,1/8,1/16,1.5000,1.7500,[v1,staff1])-note(n8,71,1316,1410,65,1,1). snote(n8,[C,n],5,1:2,3/16,1/16,1.7500,2.0000,[v1,staff1])-note(n9,72,1420,1490,61,1,1). +badly_formatted_line-note(n11,81,1556,1637,55,1,1). snote(n11,[A,n],5,2:1,0,1/16,2.0000,2.2500,[v1,staff1])-note(n11,81,1556,1637,55,1,1). snote(n19,[E,n],4,2:1,0,1/4,2.0000,3.0000,[v5,staff2])-note(n10,64,1541,1614,75,1,1). snote(n12,[G,n],5,2:1,1/16,1/16,2.2500,2.5000,[v1,staff1])-note(n12,79,1683,1752,62,1,1). From a22b20dd859a915e14eab6c1faf0406eeef3643f Mon Sep 17 00:00:00 2001 From: sildater Date: Wed, 25 Oct 2023 06:03:29 +0000 Subject: [PATCH 19/20] Format code with black (bot) --- partitura/io/importmatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partitura/io/importmatch.py b/partitura/io/importmatch.py index 356dbd78..dcb95723 100644 --- a/partitura/io/importmatch.py +++ b/partitura/io/importmatch.py @@ -632,7 +632,7 @@ def part_from_matchfile( except (TypeError, ValueError): # no staff attribute, or staff attribute does not end with a number note_attributes["staff"] = None - + if "s" in note.ScoreAttributesList: note_attributes["voice"] = 1 elif any(a.startswith("v") for a in note.ScoreAttributesList): From 0f50a2172f43799e46b18defedfdadf56af2ead9 Mon Sep 17 00:00:00 2001 From: sildater Date: Wed, 25 Oct 2023 08:06:02 +0200 Subject: [PATCH 20/20] all MatchErrors formatted the same --- partitura/io/matchfile_base.py | 6 +++--- partitura/io/matchlines_v0.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/partitura/io/matchfile_base.py b/partitura/io/matchfile_base.py index f0f0fca9..8798de65 100644 --- a/partitura/io/matchfile_base.py +++ b/partitura/io/matchfile_base.py @@ -805,7 +805,7 @@ def prepare_kwargs_from_matchline( match_pattern = cls.identifier_pattern.search(matchline, pos=pos) if match_pattern is None: - raise MatchError("") + raise MatchError("Input match line does not fit the expected pattern.") snote = snote_class.from_matchline(matchline, version=version) kwargs = dict( @@ -853,7 +853,7 @@ def prepare_kwargs_from_matchline( match_pattern = cls.identifier_pattern.search(matchline, pos=pos) if match_pattern is None: - raise MatchError("") + raise MatchError("Input match line does not fit the expected pattern.") note = note_class.from_matchline(matchline, version=version) @@ -909,7 +909,7 @@ def prepare_kwargs_from_matchline( anchor_pattern = cls.ornament_pattern.search(matchline) if anchor_pattern is None: - raise MatchError("") + raise MatchError("Input match line does not fit the expected pattern.") anchor = interpret_as_string(anchor_pattern.group("Anchor")) note = note_class.from_matchline(matchline, version=version) diff --git a/partitura/io/matchlines_v0.py b/partitura/io/matchlines_v0.py index b1a7b80f..0cdf04e7 100644 --- a/partitura/io/matchlines_v0.py +++ b/partitura/io/matchlines_v0.py @@ -894,7 +894,7 @@ def from_matchline( anchor_pattern = cls.ornament_pattern.search(matchline) if anchor_pattern is None: - raise MatchError("") + raise MatchError("Input match line does not fit the expected pattern.") note = MatchNote.from_matchline(matchline, version=version) return cls(