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
partitura/utils/music.py
CHANGED
|
@@ -20,13 +20,15 @@ import os
|
|
|
20
20
|
try:
|
|
21
21
|
import miditok
|
|
22
22
|
from miditok.midi_tokenizer import MIDITokenizer
|
|
23
|
-
import miditoolkit
|
|
23
|
+
import miditoolkit
|
|
24
24
|
except ImportError:
|
|
25
25
|
miditok = None
|
|
26
26
|
miditoolkit = None
|
|
27
|
+
|
|
27
28
|
class MIDITokenizer(object):
|
|
28
29
|
pass
|
|
29
30
|
|
|
31
|
+
|
|
30
32
|
from partitura.utils.misc import deprecated_alias
|
|
31
33
|
|
|
32
34
|
if TYPE_CHECKING:
|
|
@@ -277,13 +279,39 @@ INTERVALCLASSES = [
|
|
|
277
279
|
for specific in ["dd", "d", "P", "A", "AA"]
|
|
278
280
|
]
|
|
279
281
|
|
|
280
|
-
INTERVAL_TO_SEMITONES = dict(
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
INTERVAL_TO_SEMITONES = dict(
|
|
283
|
+
zip(
|
|
284
|
+
INTERVALCLASSES,
|
|
285
|
+
[
|
|
286
|
+
generic + specific
|
|
287
|
+
for generic in [1, 3, 8, 10]
|
|
288
|
+
for specific in [-2, -1, 0, 1, 2, 3]
|
|
289
|
+
]
|
|
290
|
+
+ [
|
|
291
|
+
generic + specific
|
|
292
|
+
for generic in [0, 5, 7]
|
|
293
|
+
for specific in [-2, -1, 0, 1, 2]
|
|
294
|
+
],
|
|
295
|
+
)
|
|
296
|
+
)
|
|
284
297
|
|
|
285
|
-
STEPS = {"C": 0, "D": 1, "E": 2, "F": 3, "G": 4, "A": 5, "B": 6, 0: "C", 1: "D", 2: "E", 3: "F", 4: "G", 5: "A", 6: "B"}
|
|
286
298
|
|
|
299
|
+
STEPS = {
|
|
300
|
+
"C": 0,
|
|
301
|
+
"D": 1,
|
|
302
|
+
"E": 2,
|
|
303
|
+
"F": 3,
|
|
304
|
+
"G": 4,
|
|
305
|
+
"A": 5,
|
|
306
|
+
"B": 6,
|
|
307
|
+
0: "C",
|
|
308
|
+
1: "D",
|
|
309
|
+
2: "E",
|
|
310
|
+
3: "F",
|
|
311
|
+
4: "G",
|
|
312
|
+
5: "A",
|
|
313
|
+
6: "B",
|
|
314
|
+
}
|
|
287
315
|
|
|
288
316
|
|
|
289
317
|
MUSICAL_BEATS = {6: 2, 9: 3, 12: 4}
|
|
@@ -406,7 +434,7 @@ def transpose_step(step, interval, direction):
|
|
|
406
434
|
if interval == "P1":
|
|
407
435
|
pass
|
|
408
436
|
else:
|
|
409
|
-
step = STEPS[op(STEPS[step.capitalize()], interval-1)]
|
|
437
|
+
step = STEPS[op(STEPS[step.capitalize()], interval - 1)]
|
|
410
438
|
return step
|
|
411
439
|
|
|
412
440
|
|
|
@@ -419,7 +447,7 @@ def _transpose_note(note, interval):
|
|
|
419
447
|
inverval
|
|
420
448
|
|
|
421
449
|
"""
|
|
422
|
-
if interval.quality+str(interval.number) == "P1":
|
|
450
|
+
if interval.quality + str(interval.number) == "P1":
|
|
423
451
|
pass
|
|
424
452
|
else:
|
|
425
453
|
# TODO work for arbitrary octave.
|
|
@@ -438,7 +466,9 @@ def _transpose_note(note, interval):
|
|
|
438
466
|
diff_sm = tmp_pc - prev_pc if tmp_pc >= prev_pc else tmp_pc + 12 - prev_pc
|
|
439
467
|
else:
|
|
440
468
|
diff_sm = prev_pc - tmp_pc if prev_pc >= tmp_pc else prev_pc + 12 - tmp_pc
|
|
441
|
-
note.alter =
|
|
469
|
+
note.alter = (
|
|
470
|
+
INTERVAL_TO_SEMITONES[interval.quality + str(interval.number)] - diff_sm
|
|
471
|
+
)
|
|
442
472
|
|
|
443
473
|
|
|
444
474
|
def transpose(score: ScoreLike, interval: Interval) -> ScoreLike:
|
|
@@ -458,7 +488,15 @@ def transpose(score: ScoreLike, interval: Interval) -> ScoreLike:
|
|
|
458
488
|
Transposed score.
|
|
459
489
|
"""
|
|
460
490
|
import partitura.score as s
|
|
491
|
+
import sys
|
|
492
|
+
|
|
493
|
+
# Copy needs to be deep, otherwise the recursion limit will be exceeded
|
|
494
|
+
old_recursion_depth = sys.getrecursionlimit()
|
|
495
|
+
sys.setrecursionlimit(10000)
|
|
496
|
+
# Deep copy of score
|
|
461
497
|
new_score = copy.deepcopy(score)
|
|
498
|
+
# Reset recursion limit to previous value to avoid side effects
|
|
499
|
+
sys.setrecursionlimit(old_recursion_depth)
|
|
462
500
|
if isinstance(score, s.Score):
|
|
463
501
|
for part in new_score.parts:
|
|
464
502
|
transpose(part, interval)
|
|
@@ -655,6 +693,8 @@ def midi_ticks_to_seconds(
|
|
|
655
693
|
|
|
656
694
|
SIGN_TO_ALTER = {
|
|
657
695
|
"n": 0,
|
|
696
|
+
"ns": 1,
|
|
697
|
+
"nf": -1,
|
|
658
698
|
"#": 1,
|
|
659
699
|
"s": 1,
|
|
660
700
|
"ss": 2,
|
|
@@ -671,7 +711,6 @@ SIGN_TO_ALTER = {
|
|
|
671
711
|
|
|
672
712
|
|
|
673
713
|
def ensure_pitch_spelling_format(step, alter, octave):
|
|
674
|
-
|
|
675
714
|
if step.lower() not in MIDI_BASE_CLASS:
|
|
676
715
|
if step.lower() != "r":
|
|
677
716
|
raise ValueError("Invalid `step`")
|
|
@@ -961,14 +1000,12 @@ def format_symbolic_duration(symbolic_dur):
|
|
|
961
1000
|
|
|
962
1001
|
"""
|
|
963
1002
|
if symbolic_dur is None:
|
|
964
|
-
|
|
965
1003
|
return "unknown"
|
|
966
1004
|
|
|
967
1005
|
else:
|
|
968
1006
|
result = (symbolic_dur.get("type") or "") + "." * symbolic_dur.get("dots", 0)
|
|
969
1007
|
|
|
970
1008
|
if "actual_notes" in symbolic_dur and "normal_notes" in symbolic_dur:
|
|
971
|
-
|
|
972
1009
|
result += "_{}/{}".format(
|
|
973
1010
|
symbolic_dur["actual_notes"], symbolic_dur["normal_notes"]
|
|
974
1011
|
)
|
|
@@ -1712,7 +1749,6 @@ def match_note_arrays(
|
|
|
1712
1749
|
target_note_array = ensure_notearray(target_note_array)
|
|
1713
1750
|
|
|
1714
1751
|
if fields is not None:
|
|
1715
|
-
|
|
1716
1752
|
if isinstance(fields, (list, tuple)):
|
|
1717
1753
|
onset_key, duration_key = fields
|
|
1718
1754
|
elif isinstance(fields, str):
|
|
@@ -1861,7 +1897,6 @@ def remove_silence_from_performed_part(ppart):
|
|
|
1861
1897
|
for track in control_dict:
|
|
1862
1898
|
for channel in control_dict[track]:
|
|
1863
1899
|
for number, ct in control_dict[track][channel].items():
|
|
1864
|
-
|
|
1865
1900
|
cta = np.array(ct)
|
|
1866
1901
|
cinterp = interp1d(
|
|
1867
1902
|
x=cta[:, 0],
|
|
@@ -2567,7 +2602,6 @@ def note_array_from_note_list(
|
|
|
2567
2602
|
|
|
2568
2603
|
note_array = []
|
|
2569
2604
|
for note in note_list:
|
|
2570
|
-
|
|
2571
2605
|
note_info = tuple()
|
|
2572
2606
|
note_on_div = note.start.t
|
|
2573
2607
|
note_off_div = note.start.t + note.duration_tied
|
|
@@ -2762,7 +2796,6 @@ def rest_array_from_rest_list(
|
|
|
2762
2796
|
|
|
2763
2797
|
rest_array = []
|
|
2764
2798
|
for rest in rest_list:
|
|
2765
|
-
|
|
2766
2799
|
rest_info = tuple()
|
|
2767
2800
|
rest_on_div = rest.start.t
|
|
2768
2801
|
rest_off_div = rest.start.t + rest.duration_tied
|
|
@@ -2875,14 +2908,12 @@ def rec_collapse_rests(rest_array):
|
|
|
2875
2908
|
|
|
2876
2909
|
|
|
2877
2910
|
def update_note_ids_after_unfolding(part):
|
|
2878
|
-
|
|
2879
2911
|
note_id_dict = defaultdict(list)
|
|
2880
2912
|
|
|
2881
2913
|
for n in part.notes:
|
|
2882
2914
|
note_id_dict[n.id].append(n)
|
|
2883
2915
|
|
|
2884
2916
|
for nid, notes in note_id_dict.items():
|
|
2885
|
-
|
|
2886
2917
|
if nid is None:
|
|
2887
2918
|
continue
|
|
2888
2919
|
|
|
@@ -2988,9 +3019,7 @@ def performance_notearray_from_score_notearray(
|
|
|
2988
3019
|
pnote_array = np.zeros(len(snote_array), dtype=ppart_fields)
|
|
2989
3020
|
|
|
2990
3021
|
if isinstance(velocity, np.ndarray):
|
|
2991
|
-
|
|
2992
3022
|
if velocity.ndim == 2:
|
|
2993
|
-
|
|
2994
3023
|
velocity_fun = interp1d(
|
|
2995
3024
|
x=velocity[:, 0],
|
|
2996
3025
|
y=velocity[:, 1],
|
|
@@ -3026,7 +3055,6 @@ def performance_notearray_from_score_notearray(
|
|
|
3026
3055
|
iois = np.diff(unique_onsets)
|
|
3027
3056
|
|
|
3028
3057
|
if callable(bpm) or isinstance(bpm, np.ndarray):
|
|
3029
|
-
|
|
3030
3058
|
if callable(bpm):
|
|
3031
3059
|
# bpm parameter is a callable that returns a bpm value
|
|
3032
3060
|
# for each score onset
|
|
@@ -3036,7 +3064,6 @@ def performance_notearray_from_score_notearray(
|
|
|
3036
3064
|
)
|
|
3037
3065
|
|
|
3038
3066
|
elif isinstance(bpm, np.ndarray):
|
|
3039
|
-
|
|
3040
3067
|
if bpm.ndim != 2:
|
|
3041
3068
|
raise ValueError("`bpm` should be a 2D array")
|
|
3042
3069
|
|
|
@@ -3209,7 +3236,7 @@ def slice_ppart_by_time(
|
|
|
3209
3236
|
----------
|
|
3210
3237
|
ppart : `PerformedPart` object
|
|
3211
3238
|
start_time : float
|
|
3212
|
-
Starting time in seconds
|
|
3239
|
+
Starting time in seconds
|
|
3213
3240
|
end_time : float
|
|
3214
3241
|
End time in seconds
|
|
3215
3242
|
clip_note_off : bool
|
|
@@ -3234,90 +3261,98 @@ def slice_ppart_by_time(
|
|
|
3234
3261
|
# create a new (empty) instance of a PerformedPart
|
|
3235
3262
|
# single dummy note added to be able to set sustain_pedal_threshold in __init__
|
|
3236
3263
|
# -> check `adjust_offsets_w_sustain` in partitura.performance
|
|
3237
|
-
ppart_slice = PerformedPart([{
|
|
3264
|
+
# ppart_slice = PerformedPart([{"note_on": 0, "note_off": 0, "pitch": 0}])
|
|
3238
3265
|
|
|
3239
3266
|
# get ppq if PerformedPart contains it,
|
|
3240
3267
|
# else skip time_tick info when e.g. created with 'load_performance_midi'
|
|
3241
3268
|
try:
|
|
3242
3269
|
ppq = ppart.ppq
|
|
3243
|
-
ppart_slice.ppq = ppq
|
|
3244
3270
|
except AttributeError:
|
|
3245
3271
|
ppq = None
|
|
3246
3272
|
|
|
3273
|
+
controls_slice = []
|
|
3247
3274
|
if ppart.controls:
|
|
3248
|
-
controls_slice = []
|
|
3249
3275
|
for cc in ppart.controls:
|
|
3250
|
-
if cc[
|
|
3276
|
+
if cc["time"] >= start_time and cc["time"] <= end_time:
|
|
3251
3277
|
new_cc = cc.copy()
|
|
3252
|
-
new_cc[
|
|
3278
|
+
new_cc["time"] -= start_time
|
|
3253
3279
|
if ppq:
|
|
3254
|
-
new_cc[
|
|
3280
|
+
new_cc["time_tick"] = int(2 * ppq * cc["time"])
|
|
3255
3281
|
controls_slice.append(new_cc)
|
|
3256
|
-
|
|
3257
|
-
|
|
3282
|
+
|
|
3283
|
+
programs_slice = []
|
|
3258
3284
|
if ppart.programs:
|
|
3259
|
-
programs_slice = []
|
|
3260
3285
|
for pr in ppart.programs:
|
|
3261
|
-
if pr[
|
|
3286
|
+
if pr["time"] >= start_time and pr["time"] <= end_time:
|
|
3262
3287
|
new_pr = pr.copy()
|
|
3263
|
-
new_pr[
|
|
3288
|
+
new_pr["time"] -= start_time
|
|
3264
3289
|
if ppq:
|
|
3265
|
-
new_pr[
|
|
3290
|
+
new_pr["time_tick"] = int(2 * ppq * pr["time"])
|
|
3266
3291
|
programs_slice.append(new_pr)
|
|
3267
|
-
ppart_slice.programs = programs_slice
|
|
3268
3292
|
|
|
3269
3293
|
notes_slice = []
|
|
3270
3294
|
note_id = 0
|
|
3271
|
-
for note in ppart.notes:
|
|
3295
|
+
for note in ppart.notes:
|
|
3272
3296
|
# collect previous sounding notes at start_time
|
|
3273
3297
|
if note["note_on"] < start_time and note["note_off"] > start_time:
|
|
3274
3298
|
new_note = note.copy()
|
|
3275
|
-
new_note[
|
|
3299
|
+
new_note["note_on"] = 0.0
|
|
3276
3300
|
if clip_note_off:
|
|
3277
|
-
new_note[
|
|
3278
|
-
|
|
3279
|
-
|
|
3301
|
+
new_note["note_off"] = min(
|
|
3302
|
+
note["note_off"] - start_time, end_time - start_time
|
|
3303
|
+
)
|
|
3304
|
+
else:
|
|
3305
|
+
new_note["note_off"] = note["note_off"] - start_time
|
|
3280
3306
|
if ppq:
|
|
3281
|
-
new_note[
|
|
3282
|
-
new_note[
|
|
3307
|
+
new_note["note_on_tick"] = 0
|
|
3308
|
+
new_note["note_off_tick"] = int(2 * ppq * new_note["note_off"])
|
|
3283
3309
|
if reindex_notes:
|
|
3284
|
-
new_note[
|
|
3310
|
+
new_note["id"] = f"n{note_id}"
|
|
3285
3311
|
note_id += 1
|
|
3286
3312
|
notes_slice.append(new_note)
|
|
3287
3313
|
# todo - combine both cases
|
|
3288
|
-
if note[
|
|
3289
|
-
if note[
|
|
3314
|
+
if note["note_on"] >= start_time:
|
|
3315
|
+
if note["note_on"] < end_time:
|
|
3290
3316
|
new_note = note.copy()
|
|
3291
|
-
new_note[
|
|
3317
|
+
new_note["note_on"] -= start_time
|
|
3292
3318
|
if clip_note_off:
|
|
3293
|
-
new_note[
|
|
3294
|
-
|
|
3295
|
-
|
|
3319
|
+
new_note["note_off"] = min(
|
|
3320
|
+
note["note_off"] - start_time, end_time - start_time
|
|
3321
|
+
)
|
|
3322
|
+
else:
|
|
3323
|
+
new_note["note_off"] = note["note_off"] - start_time
|
|
3296
3324
|
if ppq:
|
|
3297
|
-
new_note[
|
|
3298
|
-
new_note[
|
|
3325
|
+
new_note["note_on_tick"] = int(2 * ppq * new_note["note_on"])
|
|
3326
|
+
new_note["note_off_tick"] = int(2 * ppq * new_note["note_off"])
|
|
3299
3327
|
if reindex_notes:
|
|
3300
|
-
new_note[
|
|
3328
|
+
new_note["id"] = "n" + str(note_id)
|
|
3301
3329
|
note_id += 1
|
|
3302
3330
|
notes_slice.append(new_note)
|
|
3303
|
-
# assumes notes in list are sorted by onset time
|
|
3304
|
-
else:
|
|
3305
|
-
break
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3331
|
+
# assumes notes in list are sorted by onset time
|
|
3332
|
+
else:
|
|
3333
|
+
break
|
|
3334
|
+
|
|
3335
|
+
# Create slice PerformedPart
|
|
3336
|
+
ppart_slice = PerformedPart(
|
|
3337
|
+
notes=notes_slice, programs=programs_slice, controls=controls_slice, ppq=ppq
|
|
3338
|
+
)
|
|
3339
|
+
|
|
3340
|
+
# set threshold property after creating notes list to update 'sound_offset' values
|
|
3310
3341
|
ppart_slice.sustain_pedal_threshold = ppart.sustain_pedal_threshold
|
|
3311
3342
|
|
|
3312
3343
|
if ppart.id:
|
|
3313
|
-
ppart_slice.id = ppart.id +
|
|
3344
|
+
ppart_slice.id = ppart.id + "_slice_{}s_to_{}s".format(start_time, end_time)
|
|
3314
3345
|
if ppart.part_name:
|
|
3315
|
-
ppart_slice.part_name = ppart.part_name
|
|
3346
|
+
ppart_slice.part_name = ppart.part_name
|
|
3316
3347
|
|
|
3317
3348
|
return ppart_slice
|
|
3318
3349
|
|
|
3319
3350
|
|
|
3320
|
-
def tokenize(
|
|
3351
|
+
def tokenize(
|
|
3352
|
+
score_data: ScoreLike,
|
|
3353
|
+
tokenizer: MIDITokenizer,
|
|
3354
|
+
incomplete_bar_behaviour: str = "pad_bar",
|
|
3355
|
+
):
|
|
3321
3356
|
"""
|
|
3322
3357
|
Tokenize a score using a tokenizer from miditok.
|
|
3323
3358
|
Parameters
|
|
@@ -3340,16 +3375,23 @@ def tokenize(score_data : ScoreLike, tokenizer : MIDITokenizer, incomplete_bar_b
|
|
|
3340
3375
|
"""
|
|
3341
3376
|
|
|
3342
3377
|
if miditok is None or miditoolkit is None:
|
|
3343
|
-
raise ImportError(
|
|
3378
|
+
raise ImportError(
|
|
3379
|
+
"Miditok and miditoolkit must be installed for this function to work"
|
|
3380
|
+
)
|
|
3344
3381
|
with TemporaryDirectory() as tmpdir:
|
|
3345
3382
|
temp_midi_path = os.path.join(tmpdir, "temp_midi.mid")
|
|
3346
|
-
partitura.io.exportmidi.save_score_midi(
|
|
3383
|
+
partitura.io.exportmidi.save_score_midi(
|
|
3384
|
+
score_data,
|
|
3385
|
+
out=temp_midi_path,
|
|
3386
|
+
anacrusis_behavior=incomplete_bar_behaviour,
|
|
3387
|
+
part_voice_assign_mode=4,
|
|
3388
|
+
minimum_ppq=480,
|
|
3389
|
+
)
|
|
3347
3390
|
midi = miditoolkit.MidiFile(temp_midi_path)
|
|
3348
3391
|
tokens = tokenizer(midi)
|
|
3349
3392
|
return tokens
|
|
3350
3393
|
|
|
3351
3394
|
|
|
3352
|
-
|
|
3353
3395
|
if __name__ == "__main__":
|
|
3354
3396
|
import doctest
|
|
3355
3397
|
|
partitura/utils/normalize.py
CHANGED
|
@@ -6,20 +6,23 @@ This module contains normalization utilities
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
EPSILON=0.0001
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def range_normalize(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
EPSILON = 0.0001
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def range_normalize(
|
|
13
|
+
array,
|
|
14
|
+
min_value=None,
|
|
15
|
+
max_value=None,
|
|
16
|
+
log=False,
|
|
17
|
+
log2=False,
|
|
18
|
+
exp=False,
|
|
19
|
+
exp2=False,
|
|
20
|
+
hard_clip=True,
|
|
21
|
+
):
|
|
19
22
|
"""
|
|
20
23
|
Linear mapping a vector from range [min_value, max_value] to [0, 1].
|
|
21
24
|
Preprocessing possible with log and exp.
|
|
22
|
-
Values exceeding the range [0, 1] are clipped to 0 or 1 if
|
|
25
|
+
Values exceeding the range [0, 1] are clipped to 0 or 1 if
|
|
23
26
|
clip is True, otherwise they are extrapolated.
|
|
24
27
|
"""
|
|
25
28
|
if min_value is None:
|
|
@@ -28,8 +31,12 @@ def range_normalize(array,
|
|
|
28
31
|
max_value = array.max()
|
|
29
32
|
if log:
|
|
30
33
|
array = np.log(np.abs(array) + EPSILON)
|
|
34
|
+
elif log2:
|
|
35
|
+
array = np.log2(np.abs(array) + EPSILON)
|
|
31
36
|
if exp:
|
|
32
37
|
array = np.exp(array)
|
|
38
|
+
elif exp2:
|
|
39
|
+
array = np.exp2(array)
|
|
33
40
|
# handle div by zero
|
|
34
41
|
if min_value == max_value:
|
|
35
42
|
array = np.clip(array, 0, 1)
|
|
@@ -41,17 +48,13 @@ def range_normalize(array,
|
|
|
41
48
|
return array
|
|
42
49
|
|
|
43
50
|
|
|
44
|
-
def zero_one_normalize(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
log = False,
|
|
48
|
-
exp = False,
|
|
49
|
-
clip = True):
|
|
50
|
-
|
|
51
|
+
def zero_one_normalize(
|
|
52
|
+
array, min_value=-3.0, max_value=3.0, log=False, exp=False, clip=True
|
|
53
|
+
):
|
|
51
54
|
"""
|
|
52
55
|
Compute zero mean and unit variance of a vector.
|
|
53
56
|
Preprocessing possible with log and exp.
|
|
54
|
-
Values exceeding the range [-min_value, max_value]
|
|
57
|
+
Values exceeding the range [-min_value, max_value]
|
|
55
58
|
are clipped if clip is True.
|
|
56
59
|
"""
|
|
57
60
|
|
|
@@ -76,41 +79,75 @@ def minmaxrange_normalize(array):
|
|
|
76
79
|
|
|
77
80
|
|
|
78
81
|
DEFAULT_NORM_FUNCS = {
|
|
79
|
-
"pitch": {
|
|
80
|
-
|
|
82
|
+
"pitch": {
|
|
83
|
+
"func": range_normalize,
|
|
84
|
+
"kwargs": {"min_value": 0, "max_value": 127},
|
|
85
|
+
},
|
|
86
|
+
"velocity": {
|
|
87
|
+
"func": range_normalize,
|
|
88
|
+
"kwargs": {"min_value": 0, "max_value": 127},
|
|
89
|
+
},
|
|
90
|
+
"onset_beat": {
|
|
91
|
+
"func": minmaxrange_normalize,
|
|
92
|
+
"kwargs": {},
|
|
93
|
+
},
|
|
94
|
+
"duration_beat": {
|
|
95
|
+
"func": range_normalize,
|
|
96
|
+
"kwargs": {"min_value": -3, "max_value": 3, "log2": True},
|
|
97
|
+
# ref beat = 4th -> -3 = 32nd, 3 = breve
|
|
98
|
+
},
|
|
99
|
+
"beat_period": {
|
|
100
|
+
"func": range_normalize,
|
|
101
|
+
"kwargs": {"min_value": -3, "max_value": 2, "log2": True},
|
|
102
|
+
# ref 1 second / beat -> -3 = 0.125 sec / beat , 2 = 4 sec / beat
|
|
103
|
+
},
|
|
104
|
+
"timing": {
|
|
105
|
+
"func": range_normalize,
|
|
106
|
+
"kwargs": {"min_value": -0.2, "max_value": 0.2},
|
|
107
|
+
# deviation in seconds
|
|
108
|
+
},
|
|
109
|
+
"articulation_log": {
|
|
110
|
+
"func": range_normalize,
|
|
111
|
+
"kwargs": {"min_value": -4, "max_value": 3},
|
|
112
|
+
# thia ia the ratio in base2 log -> just min max clip
|
|
113
|
+
},
|
|
81
114
|
# fill up with all note and performance features
|
|
82
115
|
}
|
|
83
116
|
|
|
84
117
|
|
|
85
|
-
def normalize(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
118
|
+
def normalize(
|
|
119
|
+
in_array,
|
|
120
|
+
norm_funcs=DEFAULT_NORM_FUNCS,
|
|
121
|
+
norm_func_fallback=minmaxrange_normalize,
|
|
122
|
+
default_value=np.inf,
|
|
123
|
+
):
|
|
91
124
|
"""
|
|
92
125
|
Normalize a note array.
|
|
93
126
|
May include note features as well as performance features.
|
|
94
127
|
All input columns must be of numeric types, everything is
|
|
95
128
|
cast to single precision float.
|
|
96
|
-
|
|
129
|
+
|
|
97
130
|
Parameters
|
|
98
131
|
----------
|
|
99
132
|
array : np.ndarray
|
|
100
133
|
The performance array to be normalized.
|
|
101
134
|
norm_funcs : dict
|
|
102
135
|
A dictionary of normalization functions for each feature.
|
|
103
|
-
|
|
136
|
+
|
|
104
137
|
Returns
|
|
105
138
|
-------
|
|
106
139
|
array : np.ndarray
|
|
107
140
|
The normalized performance array.
|
|
108
141
|
"""
|
|
109
|
-
dtype_new = np.dtype(
|
|
142
|
+
dtype_new = np.dtype(
|
|
143
|
+
{
|
|
144
|
+
"names": in_array.dtype.names,
|
|
145
|
+
"formats": [float for k in range(len(in_array.dtype.names))],
|
|
146
|
+
}
|
|
147
|
+
)
|
|
110
148
|
array = in_array.copy().astype(dtype_new)
|
|
111
149
|
|
|
112
150
|
for feature in array.dtype.names:
|
|
113
|
-
|
|
114
151
|
# use mask for non-default values and don't change default values
|
|
115
152
|
non_default_mask = array[feature] != default_value
|
|
116
153
|
|
|
@@ -120,9 +157,12 @@ def normalize(in_array,
|
|
|
120
157
|
else:
|
|
121
158
|
# check whether a normalization function is defined for the feature
|
|
122
159
|
if feature not in norm_funcs:
|
|
123
|
-
array[feature][non_default_mask] = norm_func_fallback(
|
|
160
|
+
array[feature][non_default_mask] = norm_func_fallback(
|
|
161
|
+
array[feature][non_default_mask]
|
|
162
|
+
)
|
|
124
163
|
else:
|
|
125
|
-
array[feature][non_default_mask] = norm_funcs[feature]["func"](
|
|
126
|
-
|
|
127
|
-
|
|
164
|
+
array[feature][non_default_mask] = norm_funcs[feature]["func"](
|
|
165
|
+
array[feature][non_default_mask], **norm_funcs[feature]["kwargs"]
|
|
166
|
+
)
|
|
167
|
+
|
|
128
168
|
return array
|
partitura/utils/synth.py
CHANGED
|
@@ -320,7 +320,6 @@ class DistributedHarmonics(object):
|
|
|
320
320
|
n_harmonics: int,
|
|
321
321
|
weights: Union[np.ndarray, str] = "equal",
|
|
322
322
|
) -> None:
|
|
323
|
-
|
|
324
323
|
self.n_harmonics = n_harmonics
|
|
325
324
|
self.weights = weights
|
|
326
325
|
|
|
@@ -330,7 +329,6 @@ class DistributedHarmonics(object):
|
|
|
330
329
|
self._overtones = np.arange(1, self.n_harmonics + 2)
|
|
331
330
|
|
|
332
331
|
def __call__(self, freq: float) -> Tuple[np.ndarray]:
|
|
333
|
-
|
|
334
332
|
return self._overtones * freq, self.weights
|
|
335
333
|
|
|
336
334
|
|
|
@@ -344,7 +342,6 @@ class ShepardTones(object):
|
|
|
344
342
|
min_freq: Union[float, int] = 77.8,
|
|
345
343
|
max_freq: Union[float, int] = 2349,
|
|
346
344
|
) -> None:
|
|
347
|
-
|
|
348
345
|
self.min_freq = min_freq
|
|
349
346
|
self.max_freq = max_freq
|
|
350
347
|
|
|
@@ -361,7 +358,6 @@ class ShepardTones(object):
|
|
|
361
358
|
)
|
|
362
359
|
|
|
363
360
|
def __call__(self, freq) -> Tuple[np.ndarray]:
|
|
364
|
-
|
|
365
361
|
min_freq = self.min_f(freq)
|
|
366
362
|
|
|
367
363
|
freqs = 2 ** np.arange(5) * min_freq
|
|
@@ -485,15 +481,13 @@ def synthesize(
|
|
|
485
481
|
return x, 1
|
|
486
482
|
|
|
487
483
|
elif isinstance(harmonic_dist, int):
|
|
488
|
-
|
|
489
484
|
harmonic_dist = DistributedHarmonics(harmonic_dist)
|
|
490
485
|
|
|
491
486
|
elif isinstance(harmonic_dist, str):
|
|
492
487
|
if harmonic_dist in ("shepard",):
|
|
493
488
|
harmonic_dist = ShepardTones()
|
|
494
489
|
|
|
495
|
-
for
|
|
496
|
-
|
|
490
|
+
for f, oif, dur in zip(freq_in_hz, onsets_in_frames, duration):
|
|
497
491
|
freqs, weights = harmonic_dist(f)
|
|
498
492
|
|
|
499
493
|
note = additive_synthesis(
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: partitura
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: A package for handling symbolic musical information
|
|
5
5
|
Home-page: https://github.com/CPJKU/partitura
|
|
6
|
-
Author: Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier
|
|
6
|
+
Author: Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier, Patricia Hu
|
|
7
7
|
Author-email: partitura-users@googlegroups.com
|
|
8
8
|
License: Apache 2.0
|
|
9
9
|
Keywords: music notation musicxml midi
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
partitura/__init__.py,sha256=pwZEYD4DiL5_xm7rioL1UsHkeMUYE9gu-063KGvkcZM,2194
|
|
2
|
+
partitura/directions.py,sha256=F5Au1u6DB0vzxscNZF5pqva73gHQzSLQ2syahyrByeY,14163
|
|
3
|
+
partitura/display.py,sha256=QPfdbvKvivfsCRSjZnWztMwRryefVn_vjuQO2BCRRxM,5599
|
|
4
|
+
partitura/performance.py,sha256=7tXXfuSbc7GPRbyg5VES-NtSrMiOFVcR3g3tO8c8n5U,21230
|
|
5
|
+
partitura/score.py,sha256=2jzdwOZG93S6_L7kCe6MRNO9W6Xe6vNm9aZlj68SSw8,156031
|
|
6
|
+
partitura/assets/musicxml.xsd,sha256=Sa6eNOBLpbcgBGh5lPcNWYMj6zNT6tti-u3Mnp3EvX8,335762
|
|
7
|
+
partitura/assets/score_example.krn,sha256=-sdx5JEZrkOyP26bmXG6XdPdgNYwHedXvM-rch5C_6Y,104
|
|
8
|
+
partitura/assets/score_example.mei,sha256=YMmQ07gztEAlkeXayTY53SSzoXVPLwoY-n4M3zpUgUY,2436
|
|
9
|
+
partitura/assets/score_example.mid,sha256=X-c10L0ore-dQu6fH67XV5LoFuSJERI_6vC0bl6JD-0,56
|
|
10
|
+
partitura/assets/score_example.musicxml,sha256=EUGM9PJzoazJLnaIqAWFFWThd7NmrZnabhkX6BSG3t8,1589
|
|
11
|
+
partitura/io/__init__.py,sha256=GWDIEmgVKYLxPaaWzEWppHHvV-VmbONab8Xz6PWaoPA,6364
|
|
12
|
+
partitura/io/exportaudio.py,sha256=3YzLFT0bKOdxLZnMwzMlp9_b9PFWBNKmiuLn5gNcLvs,3985
|
|
13
|
+
partitura/io/exportmatch.py,sha256=Wxnw54wcnN-zwPvXH7ThP7D0MLXAiHJ_NK59fwXMrlw,20946
|
|
14
|
+
partitura/io/exportmei.py,sha256=51kzU5WB41j-d2HSbNcMS6zTl4fgS-rF0bj-IOysmLQ,71850
|
|
15
|
+
partitura/io/exportmidi.py,sha256=tftlWDqFfgVYZ8j9FnOblVicFOv0zdO5sMlcZGcfILw,19725
|
|
16
|
+
partitura/io/exportmusicxml.py,sha256=d3atEddXecKUtPtdaINZMpGnu5RoNSbSrFG3J8RHEKg,35318
|
|
17
|
+
partitura/io/exportparangonada.py,sha256=AFZeYWRPFkoMoF4eMRDyZavC7v3H4Crs9bCkow2QO9s,9056
|
|
18
|
+
partitura/io/importkern.py,sha256=vwbh9Y9RlDAD5OzZywNmRQKJhtIkek8_wHfXHUB43ao,22394
|
|
19
|
+
partitura/io/importmatch.py,sha256=90spvpvUEcF3M-7vt42IwHwkpkTobyXfDS8TeUg33ZM,29868
|
|
20
|
+
partitura/io/importmei.py,sha256=wkfCmJYAZSUy-uSh1mxmvs3iTtOH5aeQxb0Cf5Rn7r8,47584
|
|
21
|
+
partitura/io/importmidi.py,sha256=Y75pfCQ_ZNrluNajhOhtS40geiNkA9RhxLc9NvxiNuY,27235
|
|
22
|
+
partitura/io/importmusic21.py,sha256=SZEnKukfHohys3vzVzRVJj88NWok9OTJ_a89gcQSmRY,8201
|
|
23
|
+
partitura/io/importmusicxml.py,sha256=T_CnwnIXmtwCgOXdN6gGu4fM8Tz2u1Z33lfbqZC2NJc,57658
|
|
24
|
+
partitura/io/importnakamura.py,sha256=jEBOLRhGXFcsfFVfxm7cerF0ap_jCt9PmcDp0VHNFYc,8432
|
|
25
|
+
partitura/io/importparangonada.py,sha256=9VKkwvBB66rwbiv_8URItFuvQ5X9N0ybpL0eHYdAMeg,5927
|
|
26
|
+
partitura/io/matchfile_base.py,sha256=mkHwL9IxJ3JhU86ob2yi1FyNKxEeXxhInLi0gTCA7sg,31670
|
|
27
|
+
partitura/io/matchfile_utils.py,sha256=ug-c9J_xP-_gvDufBPSw9PAjTFEmqE56Vr89zdcT6dE,26601
|
|
28
|
+
partitura/io/matchlines_v0.py,sha256=TVJN0YUevmSpANNjBdaoGtN-c3IrhTCcGk7WAuoOdTA,29228
|
|
29
|
+
partitura/io/matchlines_v1.py,sha256=kHxaeOQTs87yZyNTHRth5Z9BEug0-o2YUpF9pPr9P54,40486
|
|
30
|
+
partitura/io/musescore.py,sha256=kaWpRnNIxSSyfHanQa6c6CxKSDYNfWhLUbhETzLJzyU,8853
|
|
31
|
+
partitura/musicanalysis/__init__.py,sha256=XQDeUrjitlzybIP1l8CU6vOLqNm7C7B7vhe0pySwuVY,1236
|
|
32
|
+
partitura/musicanalysis/key_identification.py,sha256=Kl4hx_AkuRQuBGvNlpGAE3MqpznmWLGFo68Yx3rqSss,6991
|
|
33
|
+
partitura/musicanalysis/meter.py,sha256=ORynu2foZmmedj1je-em1xCvzPfIj9RqnZBFWnnwe5k,11189
|
|
34
|
+
partitura/musicanalysis/note_array_to_score.py,sha256=sCFxJS98t8mpL4hNHCcvArduqm6isNnQ_Zhfw7RLwVQ,21781
|
|
35
|
+
partitura/musicanalysis/note_features.py,sha256=XUOC1jUgddyTyxP5yVCLcygtY44e1tJzFcafmKdsMu0,37809
|
|
36
|
+
partitura/musicanalysis/performance_codec.py,sha256=wkf_lPLfGkWKKU-rEsWSqHj-AJAuSURRJDWawnthnXA,34663
|
|
37
|
+
partitura/musicanalysis/performance_features.py,sha256=beRKADPJefzQ2rZxM_31s_bwmlr5MTX__cvqNbOcZsc,17909
|
|
38
|
+
partitura/musicanalysis/pitch_spelling.py,sha256=u3-r_1oTOz87aEDtEkS_jVbtzrJmAo1quPqgZfmsiAk,8928
|
|
39
|
+
partitura/musicanalysis/tonal_tension.py,sha256=0e_w6E4zp3Un_ctrUc5GeLti0qZHupywzh9e2w7o3Fs,17350
|
|
40
|
+
partitura/musicanalysis/voice_separation.py,sha256=Uxy04j-Tar_hVFUY9gIcEG9qr7o-ya8ZSauuX6q_G5A,35740
|
|
41
|
+
partitura/utils/__init__.py,sha256=WebirkSldT9htYdiUwemm5Vt9uXRsaYGSDRWUm4NeTI,1973
|
|
42
|
+
partitura/utils/generic.py,sha256=T-gtDIF_qxcUgq9BUhHmsIiYLYSyyAs6Qu25DZ1EL3c,19050
|
|
43
|
+
partitura/utils/misc.py,sha256=CQrIfETS-__2ZGz0eV2BDZtd9FbUHgevQT1PSRL9VvU,7064
|
|
44
|
+
partitura/utils/music.py,sha256=TJkF6L7_gQbxLIjFWwvtcdCi6kPWsmdbZyhI5_C6LMM,111276
|
|
45
|
+
partitura/utils/normalize.py,sha256=18OvJBiYOQoefedgxpX8TEwxEqoO7hQ31XXx3JjfYps,4667
|
|
46
|
+
partitura/utils/synth.py,sha256=9xg6sVRmz7KWGlNTWFp3bo-7bT1wRwNRdi-ODr8H4PI,15568
|
|
47
|
+
partitura-1.4.0.dist-info/LICENSE,sha256=mfxqxedyDV9qko2GrppTJVlVnpK7qkUE5sPUkGK_a9I,11429
|
|
48
|
+
partitura-1.4.0.dist-info/METADATA,sha256=lQDI6LrW9bLmKh5Kk9r5CP6MCbdNNprGs98KHLpYaO4,9423
|
|
49
|
+
partitura-1.4.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
50
|
+
partitura-1.4.0.dist-info/top_level.txt,sha256=oUqKOhAhzAD7e9Al23ZKsGssQFCu9FpYj_DHGC15euU,10
|
|
51
|
+
partitura-1.4.0.dist-info/RECORD,,
|