partitura 1.3.0__py3-none-any.whl → 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- partitura/directions.py +3 -0
- partitura/display.py +0 -1
- partitura/io/__init__.py +41 -35
- partitura/io/exportmatch.py +52 -10
- partitura/io/exportmidi.py +37 -19
- partitura/io/exportmusicxml.py +6 -92
- partitura/io/exportparangonada.py +18 -19
- partitura/io/importkern.py +2 -4
- partitura/io/importmatch.py +121 -39
- partitura/io/importmei.py +161 -34
- partitura/io/importmidi.py +23 -14
- partitura/io/importmusic21.py +0 -1
- partitura/io/importmusicxml.py +48 -63
- partitura/io/importparangonada.py +0 -1
- partitura/io/matchfile_base.py +0 -21
- partitura/io/matchfile_utils.py +29 -17
- partitura/io/matchlines_v0.py +0 -22
- partitura/io/matchlines_v1.py +8 -42
- partitura/io/musescore.py +68 -41
- partitura/musicanalysis/__init__.py +1 -1
- partitura/musicanalysis/note_array_to_score.py +147 -92
- partitura/musicanalysis/note_features.py +66 -51
- partitura/musicanalysis/performance_codec.py +140 -96
- partitura/musicanalysis/performance_features.py +190 -129
- partitura/musicanalysis/pitch_spelling.py +0 -2
- partitura/musicanalysis/tonal_tension.py +0 -6
- partitura/musicanalysis/voice_separation.py +1 -22
- partitura/performance.py +178 -5
- partitura/score.py +154 -74
- partitura/utils/__init__.py +1 -1
- partitura/utils/generic.py +3 -7
- partitura/utils/misc.py +0 -1
- partitura/utils/music.py +108 -66
- partitura/utils/normalize.py +75 -35
- partitura/utils/synth.py +1 -7
- {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/METADATA +2 -2
- partitura-1.4.0.dist-info/RECORD +51 -0
- {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/WHEEL +1 -1
- partitura-1.3.0.dist-info/RECORD +0 -51
- {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/LICENSE +0 -0
- {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -19,7 +19,7 @@ __all__ = [
|
|
|
19
19
|
"list_note_feature_functions",
|
|
20
20
|
"print_note_feats_functions",
|
|
21
21
|
"print_note_feature_functions",
|
|
22
|
-
"make_note_feats",
|
|
22
|
+
"make_note_feats",
|
|
23
23
|
"make_note_features",
|
|
24
24
|
"make_rest_feats",
|
|
25
25
|
"make_rest_features",
|
|
@@ -81,7 +81,6 @@ def make_note_features(
|
|
|
81
81
|
include_empty_features: bool = True,
|
|
82
82
|
force_fixed_size: bool = False,
|
|
83
83
|
) -> Tuple[np.ndarray, List]:
|
|
84
|
-
|
|
85
84
|
"""Compute the specified feature functions for a part.
|
|
86
85
|
|
|
87
86
|
The function returns the computed feature functions as a N x M
|
|
@@ -143,16 +142,29 @@ def make_note_features(
|
|
|
143
142
|
)
|
|
144
143
|
|
|
145
144
|
for bf in feature_functions:
|
|
145
|
+
# skip time_signature_feature if force_fixed_size is True
|
|
146
|
+
if force_fixed_size and (
|
|
147
|
+
bf == "time_signature_feature" or bf == time_signature_feature
|
|
148
|
+
):
|
|
149
|
+
continue
|
|
150
|
+
# skip metrical_feature if force_fixed_size is True
|
|
151
|
+
if force_fixed_size and (bf == "metrical_feature" or bf == metrical_feature):
|
|
152
|
+
continue
|
|
153
|
+
|
|
146
154
|
if isinstance(bf, str):
|
|
147
155
|
# get function by name from module
|
|
148
156
|
func = getattr(sys.modules[__name__], bf)
|
|
149
157
|
elif isinstance(bf, types.FunctionType):
|
|
150
158
|
func = bf
|
|
151
|
-
elif force_fixed_size and bf == "time_signature_feature":
|
|
152
|
-
continue
|
|
153
159
|
else:
|
|
154
160
|
warnings.warn("Ignoring unknown feature function {}".format(bf))
|
|
155
|
-
bf, bn = func(
|
|
161
|
+
bf, bn = func(
|
|
162
|
+
na,
|
|
163
|
+
part,
|
|
164
|
+
include_empty_features=(
|
|
165
|
+
True if force_fixed_size else include_empty_features
|
|
166
|
+
),
|
|
167
|
+
)
|
|
156
168
|
# check if the size and number of the feature function are correct
|
|
157
169
|
if bf.size != 0:
|
|
158
170
|
if bf.shape[1] != len(bn):
|
|
@@ -320,13 +332,14 @@ print_note_feature_functions = print_note_feats_functions
|
|
|
320
332
|
|
|
321
333
|
|
|
322
334
|
def compute_note_array(
|
|
323
|
-
part
|
|
335
|
+
part: ScoreLike,
|
|
324
336
|
include_pitch_spelling=False,
|
|
325
337
|
include_key_signature=False,
|
|
326
338
|
include_time_signature=False,
|
|
327
339
|
include_metrical_position=False,
|
|
328
340
|
include_grace_notes=False,
|
|
329
341
|
feature_functions=None,
|
|
342
|
+
force_fixed_size=False,
|
|
330
343
|
):
|
|
331
344
|
"""
|
|
332
345
|
Create an extended note array from this part.
|
|
@@ -376,6 +389,10 @@ def compute_note_array(
|
|
|
376
389
|
the functions themselves or the names of a feature function as
|
|
377
390
|
strings (or a mix). The feature functions specified by name are
|
|
378
391
|
looked up in the `featuremixer.featurefunctions` module.
|
|
392
|
+
force_fixed_size : bool (default: False)
|
|
393
|
+
If True, the output array uses only features that have a fixed
|
|
394
|
+
size with no new entries added.
|
|
395
|
+
|
|
379
396
|
|
|
380
397
|
Returns:
|
|
381
398
|
|
|
@@ -395,12 +412,14 @@ def compute_note_array(
|
|
|
395
412
|
)
|
|
396
413
|
|
|
397
414
|
if feature_functions is not None:
|
|
398
|
-
feature_data_struct = make_note_feats(
|
|
415
|
+
feature_data_struct = make_note_feats(
|
|
416
|
+
part, feature_functions, add_idx=True, force_fixed_size=force_fixed_size
|
|
417
|
+
)
|
|
399
418
|
note_array_joined = np.lib.recfunctions.join_by("id", na, feature_data_struct)
|
|
400
419
|
note_array = note_array_joined.data
|
|
401
|
-
sort_idx = np.lexsort(
|
|
402
|
-
|
|
403
|
-
|
|
420
|
+
sort_idx = np.lexsort(
|
|
421
|
+
(note_array["duration_div"], note_array["pitch"], note_array["onset_div"])
|
|
422
|
+
)
|
|
404
423
|
note_array = note_array[sort_idx]
|
|
405
424
|
else:
|
|
406
425
|
note_array = na
|
|
@@ -499,7 +518,11 @@ def grace_feature(na, part, **kwargs):
|
|
|
499
518
|
grace = grace_notes[i]
|
|
500
519
|
n_grace = np.count_nonzero(grace_notes["onset_beat"] == grace["onset_beat"])
|
|
501
520
|
W[index, 1] = n_grace
|
|
502
|
-
W[index, 2] =
|
|
521
|
+
W[index, 2] = (
|
|
522
|
+
n_grace - sum(1 for _ in notes[grace["id"]].iter_grace_seq()) + 1
|
|
523
|
+
if grace["id"] not in (None, "None", "")
|
|
524
|
+
else 0
|
|
525
|
+
)
|
|
503
526
|
return W, feature_names
|
|
504
527
|
|
|
505
528
|
|
|
@@ -533,6 +556,7 @@ def loudness_direction_feature(na, part, **kwargs):
|
|
|
533
556
|
else:
|
|
534
557
|
force_size = False
|
|
535
558
|
if force_size:
|
|
559
|
+
|
|
536
560
|
def to_name(d):
|
|
537
561
|
if isinstance(d, score.ConstantLoudnessDirection):
|
|
538
562
|
if d.text in constant:
|
|
@@ -548,7 +572,9 @@ def loudness_direction_feature(na, part, **kwargs):
|
|
|
548
572
|
return "loudness_incr"
|
|
549
573
|
elif isinstance(d, score.DecreasingLoudnessDirection):
|
|
550
574
|
return "loudness_decr"
|
|
575
|
+
|
|
551
576
|
else:
|
|
577
|
+
|
|
552
578
|
def to_name(d):
|
|
553
579
|
if isinstance(d, score.ConstantLoudnessDirection):
|
|
554
580
|
return d.text
|
|
@@ -559,7 +585,6 @@ def loudness_direction_feature(na, part, **kwargs):
|
|
|
559
585
|
elif isinstance(d, score.DecreasingLoudnessDirection):
|
|
560
586
|
return "loudness_decr"
|
|
561
587
|
|
|
562
|
-
|
|
563
588
|
feature_by_name = {}
|
|
564
589
|
for d in directions:
|
|
565
590
|
j, bf = feature_by_name.setdefault(
|
|
@@ -595,18 +620,32 @@ def tempo_direction_feature(na, part, **kwargs):
|
|
|
595
620
|
"""
|
|
596
621
|
onsets = na["onset_div"]
|
|
597
622
|
N = len(onsets)
|
|
598
|
-
constant = [
|
|
599
|
-
|
|
600
|
-
|
|
623
|
+
constant = [
|
|
624
|
+
"adagio",
|
|
625
|
+
"largo",
|
|
626
|
+
"lento",
|
|
627
|
+
"grave",
|
|
628
|
+
"larghetto",
|
|
629
|
+
"adagietto",
|
|
630
|
+
"andante",
|
|
631
|
+
"andantino",
|
|
632
|
+
"moderato",
|
|
633
|
+
"allegretto",
|
|
634
|
+
"allegro",
|
|
635
|
+
"vivace",
|
|
636
|
+
"presto",
|
|
637
|
+
"prestissimo",
|
|
638
|
+
"unknown_constant",
|
|
639
|
+
]
|
|
601
640
|
names = constant + ["tempo_incr", "tempo_decr"]
|
|
602
641
|
directions = list(part.iter_all(score.TempoDirection, include_subclasses=True))
|
|
603
642
|
|
|
604
|
-
|
|
605
643
|
if "include_empty_features" in kwargs.keys():
|
|
606
644
|
force_size = kwargs["include_empty_features"]
|
|
607
645
|
else:
|
|
608
646
|
force_size = False
|
|
609
647
|
if force_size:
|
|
648
|
+
|
|
610
649
|
def to_name(d):
|
|
611
650
|
if isinstance(d, score.ResetTempoDirection):
|
|
612
651
|
ref = d.reference_tempo
|
|
@@ -629,7 +668,9 @@ def tempo_direction_feature(na, part, **kwargs):
|
|
|
629
668
|
return "tempo_incr"
|
|
630
669
|
elif isinstance(d, score.DecreasingTempoDirection):
|
|
631
670
|
return "tempo_decr"
|
|
671
|
+
|
|
632
672
|
else:
|
|
673
|
+
|
|
633
674
|
def to_name(d):
|
|
634
675
|
if isinstance(d, score.ResetTempoDirection):
|
|
635
676
|
ref = d.reference_tempo
|
|
@@ -651,7 +692,6 @@ def tempo_direction_feature(na, part, **kwargs):
|
|
|
651
692
|
)
|
|
652
693
|
bf += feature_function_activation(d)(onsets)
|
|
653
694
|
|
|
654
|
-
|
|
655
695
|
if not force_size:
|
|
656
696
|
M = len(feature_by_name) if len(feature_by_name) > 0 else 1
|
|
657
697
|
names = [None] * M
|
|
@@ -681,12 +721,15 @@ def articulation_direction_feature(na, part, **kwargs):
|
|
|
681
721
|
else:
|
|
682
722
|
force_size = False
|
|
683
723
|
if force_size:
|
|
724
|
+
|
|
684
725
|
def to_name(d):
|
|
685
726
|
if d.text in constant_names:
|
|
686
727
|
return d.text
|
|
687
728
|
else:
|
|
688
|
-
return "
|
|
729
|
+
return "unknown_articulation"
|
|
730
|
+
|
|
689
731
|
else:
|
|
732
|
+
|
|
690
733
|
def to_name(d):
|
|
691
734
|
return d.text
|
|
692
735
|
|
|
@@ -722,13 +765,8 @@ def feature_function_activation(direction):
|
|
|
722
765
|
if isinstance(
|
|
723
766
|
direction, (score.DynamicLoudnessDirection, score.DynamicTempoDirection)
|
|
724
767
|
):
|
|
725
|
-
# a dynamic direction will be encoded as a ramp from d.start.t to
|
|
726
|
-
# d.end
|
|
727
|
-
# constant direction.
|
|
728
|
-
|
|
729
|
-
# There are two potential issues:
|
|
730
|
-
|
|
731
|
-
# Issue 1. d.end is None (e.g. just a ritardando without dashes). In this case
|
|
768
|
+
# a dynamic direction will be encoded as a ramp from d.start.t to d.end.t
|
|
769
|
+
# if d.end is None (e.g. just a ritardando without dashes)
|
|
732
770
|
if direction.end:
|
|
733
771
|
direction_end = direction.end.t
|
|
734
772
|
else:
|
|
@@ -738,31 +776,10 @@ def feature_function_activation(direction):
|
|
|
738
776
|
direction_end = measure.start.t
|
|
739
777
|
else:
|
|
740
778
|
# no measure, unlikely, but not impossible.
|
|
741
|
-
direction_end = direction.start.t
|
|
742
|
-
|
|
743
|
-
if isinstance(direction, score.TempoDirection):
|
|
744
|
-
next_dir = next(
|
|
745
|
-
direction.start.iter_next(score.ConstantTempoDirection), None
|
|
746
|
-
)
|
|
747
|
-
if isinstance(direction, score.ArticulationDirection):
|
|
748
|
-
next_dir = next(
|
|
749
|
-
direction.start.iter_next(score.ConstantArticulationDirection), None
|
|
750
|
-
)
|
|
751
|
-
else:
|
|
752
|
-
next_dir = next(
|
|
753
|
-
direction.start.iter_next(score.ConstantLoudnessDirection), None
|
|
754
|
-
)
|
|
779
|
+
direction_end = direction.start.t + 1
|
|
755
780
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
sustained_end = next_dir.start.t
|
|
759
|
-
else:
|
|
760
|
-
# Issue 2. there is no next constant direction. In that case the
|
|
761
|
-
# feature function will be a ramp with a quarter note ramp
|
|
762
|
-
sustained_end = direction_end + direction.start.quarter
|
|
763
|
-
|
|
764
|
-
x = [direction.start.t, direction_end - epsilon, sustained_end - epsilon]
|
|
765
|
-
y = [0, 1, 1]
|
|
781
|
+
x = [direction.start.t, direction_end, direction_end + epsilon]
|
|
782
|
+
y = [0, 1, 0]
|
|
766
783
|
|
|
767
784
|
elif isinstance(
|
|
768
785
|
direction,
|
|
@@ -933,7 +950,6 @@ def ornament_feature(na, part, **kwargs):
|
|
|
933
950
|
names = [None] * M
|
|
934
951
|
W = np.zeros((N, M))
|
|
935
952
|
|
|
936
|
-
|
|
937
953
|
for name, (j, bf) in feature_by_name.items():
|
|
938
954
|
if fix_size:
|
|
939
955
|
j = names.index(name)
|
|
@@ -999,7 +1015,6 @@ def metrical_feature(na, part, **kwargs):
|
|
|
999
1015
|
eps = 10**-6
|
|
1000
1016
|
|
|
1001
1017
|
for i, n in enumerate(notes):
|
|
1002
|
-
|
|
1003
1018
|
beats, beat_type, mus_beats = ts_map(n.start.t).astype(int)
|
|
1004
1019
|
measure = next(n.start.iter_prev(score.Measure, eq=True), None)
|
|
1005
1020
|
|