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.
Files changed (41) hide show
  1. partitura/directions.py +3 -0
  2. partitura/display.py +0 -1
  3. partitura/io/__init__.py +41 -35
  4. partitura/io/exportmatch.py +52 -10
  5. partitura/io/exportmidi.py +37 -19
  6. partitura/io/exportmusicxml.py +6 -92
  7. partitura/io/exportparangonada.py +18 -19
  8. partitura/io/importkern.py +2 -4
  9. partitura/io/importmatch.py +121 -39
  10. partitura/io/importmei.py +161 -34
  11. partitura/io/importmidi.py +23 -14
  12. partitura/io/importmusic21.py +0 -1
  13. partitura/io/importmusicxml.py +48 -63
  14. partitura/io/importparangonada.py +0 -1
  15. partitura/io/matchfile_base.py +0 -21
  16. partitura/io/matchfile_utils.py +29 -17
  17. partitura/io/matchlines_v0.py +0 -22
  18. partitura/io/matchlines_v1.py +8 -42
  19. partitura/io/musescore.py +68 -41
  20. partitura/musicanalysis/__init__.py +1 -1
  21. partitura/musicanalysis/note_array_to_score.py +147 -92
  22. partitura/musicanalysis/note_features.py +66 -51
  23. partitura/musicanalysis/performance_codec.py +140 -96
  24. partitura/musicanalysis/performance_features.py +190 -129
  25. partitura/musicanalysis/pitch_spelling.py +0 -2
  26. partitura/musicanalysis/tonal_tension.py +0 -6
  27. partitura/musicanalysis/voice_separation.py +1 -22
  28. partitura/performance.py +178 -5
  29. partitura/score.py +154 -74
  30. partitura/utils/__init__.py +1 -1
  31. partitura/utils/generic.py +3 -7
  32. partitura/utils/misc.py +0 -1
  33. partitura/utils/music.py +108 -66
  34. partitura/utils/normalize.py +75 -35
  35. partitura/utils/synth.py +1 -7
  36. {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/METADATA +2 -2
  37. partitura-1.4.0.dist-info/RECORD +51 -0
  38. {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/WHEEL +1 -1
  39. partitura-1.3.0.dist-info/RECORD +0 -51
  40. {partitura-1.3.0.dist-info → partitura-1.4.0.dist-info}/LICENSE +0 -0
  41. {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(na, part, include_empty_features=(True if force_fixed_size else include_empty_features))
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 : ScoreLike,
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(part, feature_functions, add_idx=True)
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((note_array["duration_div"],
402
- note_array["pitch"],
403
- note_array["onset_div"]))
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] = n_grace - sum(1 for _ in notes[grace["id"]].iter_grace_seq()) + 1 if grace["id"] not in (None, 'None', "") else 0
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 = ["adagio", "largo", "lento", "grave", "larghetto", "adagietto", "andante",
599
- "andantino", "moderato", "allegretto", "allegro", "vivace", "presto", "prestissimo",
600
- "unknown_constant"]
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 "unknown_direction"
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.t, and then a step from d.end.t to the start of the next
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
- if next_dir:
757
- # TODO: what do we do when next_dir is too far away?
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