partitura 1.6.0__tar.gz → 1.7.0__tar.gz
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-1.6.0 → partitura-1.7.0}/PKG-INFO +3 -2
- {partitura-1.6.0 → partitura-1.7.0}/partitura/assets/score_example.musicxml +3 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportmatch.py +35 -26
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportmidi.py +13 -13
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportmusicxml.py +33 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importdcml.py +1 -1
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importmatch.py +42 -20
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importmidi.py +32 -31
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importmusicxml.py +50 -7
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/performance_codec.py +20 -5
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/performance_features.py +23 -2
- {partitura-1.6.0 → partitura-1.7.0}/partitura/performance.py +37 -15
- {partitura-1.6.0 → partitura-1.7.0}/partitura/score.py +211 -84
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/__init__.py +0 -1
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/generic.py +50 -45
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/misc.py +2 -2
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/music.py +322 -89
- {partitura-1.6.0 → partitura-1.7.0}/partitura.egg-info/PKG-INFO +3 -2
- {partitura-1.6.0 → partitura-1.7.0}/partitura.egg-info/SOURCES.txt +1 -0
- {partitura-1.6.0 → partitura-1.7.0}/setup.py +1 -1
- partitura-1.7.0/tests/test_iter.py +164 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_midi_import.py +136 -1
- partitura-1.7.0/tests/test_part_properties.py +103 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_performance.py +1 -1
- partitura-1.7.0/tests/test_transpose.py +43 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_utils.py +65 -1
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_xml.py +25 -12
- partitura-1.6.0/tests/test_part_properties.py +0 -38
- partitura-1.6.0/tests/test_transpose.py +0 -26
- {partitura-1.6.0 → partitura-1.7.0}/LICENSE +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/README.md +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/__init__.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/assets/musicxml.xsd +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/assets/score_example.krn +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/assets/score_example.mei +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/assets/score_example.mid +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/directions.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/display.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/__init__.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportaudio.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportkern.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportmei.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/exportparangonada.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importkern.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importmei.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importmusic21.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importnakamura.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/importparangonada.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/matchfile_base.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/matchfile_utils.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/matchlines_v0.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/matchlines_v1.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/io/musescore.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/__init__.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/key_identification.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/meter.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/note_array_to_score.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/note_features.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/pitch_spelling.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/tonal_tension.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/musicanalysis/voice_separation.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/fluidsynth.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/globals.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/normalize.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura/utils/synth.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura.egg-info/dependency_links.txt +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura.egg-info/requires.txt +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/partitura.egg-info/top_level.txt +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/setup.cfg +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_clef.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_cross_staff.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_dcml_import.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_deprecations.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_display.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_fluidsynth.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_harmony.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_kern.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_key_estimation.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_load_performance.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_load_score.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_m21_import.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_match_export.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_match_import.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_mei.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_merge_parts.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_metrical_position.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_midi_export.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_musescore.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_nakamura.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_new_divs.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_note_array.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_note_features.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_octave_shift.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_parangonada.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_partial_measures.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_performance_codec.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_performance_features.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_pianoroll.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_pitch_spelling.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_quarter_adjust.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_rest_array.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_synth.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_time_estimation.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_times.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_tonal_tension.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_urlload.py +0 -0
- {partitura-1.6.0 → partitura-1.7.0}/tests/test_voice_estimation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: partitura
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: A package for handling symbolic musical information
|
|
5
5
|
Home-page: https://github.com/CPJKU/partitura
|
|
6
6
|
Author: Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier, Patricia Hu
|
|
@@ -29,6 +29,7 @@ Dynamic: description-content-type
|
|
|
29
29
|
Dynamic: home-page
|
|
30
30
|
Dynamic: keywords
|
|
31
31
|
Dynamic: license
|
|
32
|
+
Dynamic: license-file
|
|
32
33
|
Dynamic: requires-dist
|
|
33
34
|
Dynamic: requires-python
|
|
34
35
|
Dynamic: summary
|
|
@@ -70,7 +70,7 @@ def matchfile_from_alignment(
|
|
|
70
70
|
piece: Optional[str] = None,
|
|
71
71
|
score_filename: Optional[PathLike] = None,
|
|
72
72
|
performance_filename: Optional[PathLike] = None,
|
|
73
|
-
assume_part_unfolded: bool =
|
|
73
|
+
assume_part_unfolded: bool = True,
|
|
74
74
|
tempo_indication: Optional[str] = None,
|
|
75
75
|
diff_score_version_notes: Optional[list] = None,
|
|
76
76
|
version: Version = LATEST_VERSION,
|
|
@@ -107,7 +107,7 @@ def matchfile_from_alignment(
|
|
|
107
107
|
Whether to assume that the part has been unfolded according to the
|
|
108
108
|
repetitions in the alignment. If False, the part will be automatically
|
|
109
109
|
unfolded to have maximal coverage of the notes in the alignment.
|
|
110
|
-
See `partitura.score.unfold_part_alignment
|
|
110
|
+
See `partitura.score.unfold_part_alignment`, defaults to True.
|
|
111
111
|
tempo_indication : str or None
|
|
112
112
|
The tempo direction indicated in the beginning of the score
|
|
113
113
|
diff_score_version_notes : list or None
|
|
@@ -210,18 +210,28 @@ def matchfile_from_alignment(
|
|
|
210
210
|
# Info for sorting lines
|
|
211
211
|
snote_sort_info = dict()
|
|
212
212
|
for (mnum, msd, msb), m in zip(measure_starts, measures):
|
|
213
|
+
if mnum == 0:
|
|
214
|
+
# handle offsets in anacrusis measure
|
|
215
|
+
ts_num, ts_den, _ = spart.time_signature_map(0)
|
|
216
|
+
dpq = int(spart.quarter_duration_map(0))
|
|
217
|
+
measure_dur_in_divs = m.end.t - m.start.t
|
|
218
|
+
expected_measure_dur = ts_num * 4 / ts_den * dpq
|
|
219
|
+
if measure_dur_in_divs < expected_measure_dur:
|
|
220
|
+
msd -= expected_measure_dur - measure_dur_in_divs
|
|
221
|
+
msb -= (expected_measure_dur - measure_dur_in_divs) / dpq * ts_den / 4
|
|
222
|
+
|
|
213
223
|
time_signatures = spart.iter_all(score.TimeSignature, m.start, m.end)
|
|
214
224
|
|
|
215
225
|
for tsig in time_signatures:
|
|
216
226
|
time_divs = int(tsig.start.t)
|
|
217
227
|
time_beats = float(beat_map(time_divs))
|
|
228
|
+
ts_num, ts_den, _ = spart.time_signature_map(tsig.start.t)
|
|
218
229
|
dpq = int(spart.quarter_duration_map(time_divs))
|
|
230
|
+
divs_per_beat = 4 / ts_den * dpq
|
|
219
231
|
beat = int((time_beats - msb) // 1)
|
|
220
232
|
|
|
221
|
-
ts_num, ts_den, _ = spart.time_signature_map(tsig.start.t)
|
|
222
|
-
|
|
223
233
|
moffset_divs = Fraction(
|
|
224
|
-
int(time_divs - msd - beat *
|
|
234
|
+
int(time_divs - msd - beat * divs_per_beat), int(ts_den * divs_per_beat)
|
|
225
235
|
)
|
|
226
236
|
|
|
227
237
|
scoreprop_lines["time_signatures"].append(
|
|
@@ -247,15 +257,15 @@ def matchfile_from_alignment(
|
|
|
247
257
|
key_signatures = spart.iter_all(score.KeySignature, m.start, m.end)
|
|
248
258
|
|
|
249
259
|
for ksig in key_signatures:
|
|
250
|
-
time_divs = int(
|
|
260
|
+
time_divs = int(ksig.start.t)
|
|
251
261
|
time_beats = float(beat_map(time_divs))
|
|
262
|
+
ts_num, ts_den, _ = spart.time_signature_map(ksig.start.t)
|
|
252
263
|
dpq = int(spart.quarter_duration_map(time_divs))
|
|
264
|
+
divs_per_beat = 4 / ts_den * dpq
|
|
253
265
|
beat = int((time_beats - msb) // 1)
|
|
254
266
|
|
|
255
|
-
ts_num, ts_den, _ = spart.time_signature_map(tsig.start.t)
|
|
256
|
-
|
|
257
267
|
moffset_divs = Fraction(
|
|
258
|
-
int(time_divs - msd - beat *
|
|
268
|
+
int(time_divs - msd - beat * divs_per_beat), int(ts_den * divs_per_beat)
|
|
259
269
|
)
|
|
260
270
|
|
|
261
271
|
scoreprop_lines["key_signatures"].append(
|
|
@@ -282,27 +292,28 @@ def matchfile_from_alignment(
|
|
|
282
292
|
snotes = spart.iter_all(score.Note, m.start, m.end, include_subclasses=True)
|
|
283
293
|
# Beginning of each measure
|
|
284
294
|
for snote in snotes:
|
|
285
|
-
onset_divs
|
|
295
|
+
onset_divs = snote.start.t
|
|
296
|
+
offset_divs = snote.start.t + snote.duration_tied
|
|
286
297
|
duration_divs = offset_divs - onset_divs
|
|
287
|
-
|
|
298
|
+
# beat computations
|
|
288
299
|
onset_beats, offset_beats = beat_map([onset_divs, offset_divs])
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
beat = int((onset_beats - msb) // 1)
|
|
293
|
-
|
|
300
|
+
duration_beats = offset_beats - onset_beats
|
|
301
|
+
beat = int((onset_beats - msb) // 1) # beat field of the snote
|
|
302
|
+
# quarter, div, symbolic computation
|
|
294
303
|
ts_num, ts_den, _ = spart.time_signature_map(snote.start.t)
|
|
295
|
-
|
|
296
|
-
duration_symb = Fraction(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
moffset_divs = Fraction(
|
|
304
|
+
dpq = int(spart.quarter_duration_map(onset_divs))
|
|
305
|
+
duration_symb = Fraction(
|
|
306
|
+
duration_divs, dpq * 4
|
|
307
|
+
) # compute duration from quarters/divs
|
|
308
|
+
divs_per_beat = 4 / ts_den * dpq
|
|
309
|
+
moffset_divs = Fraction(
|
|
310
|
+
int(onset_divs - msd - beat * divs_per_beat),
|
|
311
|
+
int(ts_den * divs_per_beat),
|
|
312
|
+
)
|
|
301
313
|
|
|
302
314
|
if debug:
|
|
303
|
-
duration_beats = offset_beats - onset_beats
|
|
304
315
|
moffset_beat = (onset_beats - msb - beat) / ts_den
|
|
305
|
-
assert np.isclose(float(duration_symb), duration_beats)
|
|
316
|
+
assert np.isclose(float(duration_symb), duration_beats / ts_den)
|
|
306
317
|
assert np.isclose(moffset_beat, float(moffset_divs))
|
|
307
318
|
|
|
308
319
|
score_attributes_list = []
|
|
@@ -508,9 +519,7 @@ def matchfile_from_alignment(
|
|
|
508
519
|
|
|
509
520
|
# Concatenate all lines
|
|
510
521
|
all_match_lines += list(note_lines) + pedal_lines
|
|
511
|
-
|
|
512
522
|
matchfile = MatchFile(lines=all_match_lines)
|
|
513
|
-
|
|
514
523
|
return matchfile
|
|
515
524
|
|
|
516
525
|
|
|
@@ -36,34 +36,34 @@ def map_to_track_channel(note_keys, mode):
|
|
|
36
36
|
if mode == 0:
|
|
37
37
|
trk = tr_helper.setdefault(p, len(tr_helper))
|
|
38
38
|
ch1 = ch_helper.setdefault(p, {})
|
|
39
|
-
ch2 = ch1.setdefault(v, len(ch1)
|
|
39
|
+
ch2 = ch1.setdefault(v, len(ch1))
|
|
40
40
|
track[(pg, p, v)] = trk
|
|
41
41
|
channel[(pg, p, v)] = ch2
|
|
42
42
|
elif mode == 1:
|
|
43
43
|
trk = tr_helper.setdefault(pg, len(tr_helper))
|
|
44
44
|
ch1 = ch_helper.setdefault(pg, {})
|
|
45
|
-
ch2 = ch1.setdefault(p, len(ch1)
|
|
45
|
+
ch2 = ch1.setdefault(p, len(ch1))
|
|
46
46
|
track[(pg, p, v)] = trk
|
|
47
47
|
channel[(pg, p, v)] = ch2
|
|
48
48
|
elif mode == 2:
|
|
49
49
|
track[(pg, p, v)] = 0
|
|
50
|
-
ch = ch_helper.setdefault(p, len(ch_helper)
|
|
50
|
+
ch = ch_helper.setdefault(p, len(ch_helper))
|
|
51
51
|
channel[(pg, p, v)] = ch
|
|
52
52
|
elif mode == 3:
|
|
53
53
|
trk = tr_helper.setdefault(p, len(tr_helper))
|
|
54
54
|
track[(pg, p, v)] = trk
|
|
55
|
-
channel[(pg, p, v)] =
|
|
55
|
+
channel[(pg, p, v)] = 0
|
|
56
56
|
elif mode == 4:
|
|
57
57
|
track[(pg, p, v)] = 0
|
|
58
|
-
channel[(pg, p, v)] =
|
|
58
|
+
channel[(pg, p, v)] = 0
|
|
59
59
|
elif mode == 5:
|
|
60
60
|
trk = tr_helper.setdefault((p, v), len(tr_helper))
|
|
61
61
|
track[(pg, p, v)] = trk
|
|
62
|
-
channel[(pg, p, v)] =
|
|
62
|
+
channel[(pg, p, v)] = 0
|
|
63
63
|
else:
|
|
64
64
|
raise Exception("unsupported part/voice assign mode {}".format(mode))
|
|
65
65
|
|
|
66
|
-
result = dict((k, (track.get(k, 0), channel.get(k,
|
|
66
|
+
result = dict((k, (track.get(k, 0), channel.get(k, 0))) for k in note_keys)
|
|
67
67
|
# for (pg, p, voice), v in result.items():
|
|
68
68
|
# pgn = pg.group_name if hasattr(pg, 'group_name') else pg.id
|
|
69
69
|
# print(pgn, p.id, voice)
|
|
@@ -177,7 +177,7 @@ def save_performance_midi(
|
|
|
177
177
|
|
|
178
178
|
for c in performed_part.controls:
|
|
179
179
|
track = c.get("track", 0)
|
|
180
|
-
ch = c.get("channel",
|
|
180
|
+
ch = c.get("channel", 0)
|
|
181
181
|
t = int(np.round(10**6 * ppq * c["time"] / mpq))
|
|
182
182
|
track_events[track][t].append(
|
|
183
183
|
Message(
|
|
@@ -190,7 +190,7 @@ def save_performance_midi(
|
|
|
190
190
|
|
|
191
191
|
for n in performed_part.notes:
|
|
192
192
|
track = n.get("track", 0)
|
|
193
|
-
ch = n.get("channel",
|
|
193
|
+
ch = n.get("channel", 0)
|
|
194
194
|
t_on = int(np.round(10**6 * ppq * n["note_on"] / mpq))
|
|
195
195
|
t_off = int(np.round(10**6 * ppq * n["note_off"] / mpq))
|
|
196
196
|
vel = n.get("velocity", default_velocity)
|
|
@@ -203,7 +203,7 @@ def save_performance_midi(
|
|
|
203
203
|
|
|
204
204
|
for p in performed_part.programs:
|
|
205
205
|
track = p.get("track", 0)
|
|
206
|
-
ch = p.get("channel",
|
|
206
|
+
ch = p.get("channel", 0)
|
|
207
207
|
t = int(np.round(10**6 * ppq * p["time"] / mpq))
|
|
208
208
|
track_events[track][t].append(
|
|
209
209
|
Message("program_change", program=int(p["program"]), channel=ch)
|
|
@@ -215,11 +215,11 @@ def save_performance_midi(
|
|
|
215
215
|
list(
|
|
216
216
|
set(
|
|
217
217
|
[
|
|
218
|
-
(c.get("channel",
|
|
218
|
+
(c.get("channel", 0), c.get("track", 0))
|
|
219
219
|
for c in performed_part.controls
|
|
220
220
|
]
|
|
221
221
|
+ [
|
|
222
|
-
(n.get("channel",
|
|
222
|
+
(n.get("channel", 0), n.get("track", 0))
|
|
223
223
|
for n in performed_part.notes
|
|
224
224
|
]
|
|
225
225
|
)
|
|
@@ -395,7 +395,7 @@ def save_score_midi(
|
|
|
395
395
|
|
|
396
396
|
def to_ppq(t):
|
|
397
397
|
# convert div times to new ppq
|
|
398
|
-
return
|
|
398
|
+
return round(ppq * (qm(t) - ftp))
|
|
399
399
|
|
|
400
400
|
for tp in part.iter_all(score.Tempo):
|
|
401
401
|
tempos[to_ppq(tp.start.t)] = MetaMessage(
|
|
@@ -36,6 +36,23 @@ ARTICULATIONS = [
|
|
|
36
36
|
"tenuto",
|
|
37
37
|
"unstress",
|
|
38
38
|
]
|
|
39
|
+
ORNAMENTS = [
|
|
40
|
+
"trill-mark",
|
|
41
|
+
"turn",
|
|
42
|
+
"delayed-turn",
|
|
43
|
+
"inverted-turn",
|
|
44
|
+
"delayed-inverted-turn",
|
|
45
|
+
"vertical-turn",
|
|
46
|
+
"inverted-vertical-turn",
|
|
47
|
+
"shake",
|
|
48
|
+
"wavy-line",
|
|
49
|
+
"mordent",
|
|
50
|
+
"inverted-mordent",
|
|
51
|
+
"schleifer",
|
|
52
|
+
"tremolo",
|
|
53
|
+
"haydn",
|
|
54
|
+
"other-ornament",
|
|
55
|
+
]
|
|
39
56
|
|
|
40
57
|
|
|
41
58
|
def range_number_from_counter(e, label, counter):
|
|
@@ -154,6 +171,16 @@ def make_note_el(note, dur, voice, counter, n_of_staves):
|
|
|
154
171
|
articulations_e.extend(articulations)
|
|
155
172
|
notations.append(articulations_e)
|
|
156
173
|
|
|
174
|
+
if note.ornaments:
|
|
175
|
+
ornaments = []
|
|
176
|
+
for ornament in note.ornaments:
|
|
177
|
+
if ornament in ORNAMENTS:
|
|
178
|
+
ornaments.append(etree.Element(ornament))
|
|
179
|
+
if ornaments:
|
|
180
|
+
ornaments_e = etree.Element("ornaments")
|
|
181
|
+
ornaments_e.extend(ornaments)
|
|
182
|
+
notations.append(ornaments_e)
|
|
183
|
+
|
|
157
184
|
if note.technical:
|
|
158
185
|
technical = []
|
|
159
186
|
for technical_notation in note.technical:
|
|
@@ -184,6 +211,8 @@ def make_note_el(note, dur, voice, counter, n_of_staves):
|
|
|
184
211
|
actual_e.text = str(sym_dur["actual_notes"])
|
|
185
212
|
normal_e = etree.SubElement(time_mod_e, "normal-notes")
|
|
186
213
|
normal_e.text = str(sym_dur["normal_notes"])
|
|
214
|
+
for _ in range(sym_dur.get("normal_dots", 0)):
|
|
215
|
+
etree.SubElement(time_mod_e, "normal-dot")
|
|
187
216
|
|
|
188
217
|
if note.staff is not None:
|
|
189
218
|
if note.staff != 1 or n_of_staves > 1:
|
|
@@ -240,12 +269,16 @@ def make_note_el(note, dur, voice, counter, n_of_staves):
|
|
|
240
269
|
tuplet_actual_notes_e.text = str(tuplet.actual_notes)
|
|
241
270
|
tuplet_actual_type_e = etree.SubElement(tuplet_actual_e, "tuplet-type")
|
|
242
271
|
tuplet_actual_type_e.text = str(tuplet.actual_type)
|
|
272
|
+
for _ in range(tuplet.actual_dots):
|
|
273
|
+
etree.SubElement(tuplet_actual_e, "tuplet-dot")
|
|
243
274
|
# tuplet-normal tag
|
|
244
275
|
tuplet_normal_e = etree.SubElement(tuplet_e, "tuplet-normal")
|
|
245
276
|
tuplet_normal_notes_e = etree.SubElement(tuplet_normal_e, "tuplet-number")
|
|
246
277
|
tuplet_normal_notes_e.text = str(tuplet.normal_notes)
|
|
247
278
|
tuplet_normal_type_e = etree.SubElement(tuplet_normal_e, "tuplet-type")
|
|
248
279
|
tuplet_normal_type_e.text = str(tuplet.normal_type)
|
|
280
|
+
for _ in range(tuplet.normal_dots):
|
|
281
|
+
etree.SubElement(tuplet_normal_e, "tuplet-dot")
|
|
249
282
|
notations.append(tuplet_e)
|
|
250
283
|
|
|
251
284
|
if notations:
|
|
@@ -284,7 +284,7 @@ def read_harmony_tsv(beat_tsv_path, part):
|
|
|
284
284
|
# key_alter = re.search(r"[#b]", row["globalkey"]).group(0) if re.search(r"[#b]", row["globalkey"]) else ""
|
|
285
285
|
# key_alter = key_alter.replace("b", "-")
|
|
286
286
|
# key_alter = ALT_TO_INT[key_alter]
|
|
287
|
-
# key_step, key_alter =
|
|
287
|
+
# key_step, key_alter, _ = transpose_note_attributes(transposition_interval, key_step, key_alter)
|
|
288
288
|
# local_key = key_step + INT_TO_ALT[key_alter]
|
|
289
289
|
part.add(
|
|
290
290
|
spt.Cadence(
|
|
@@ -226,8 +226,7 @@ def load_match(
|
|
|
226
226
|
ppart = performed_part_from_match(mf, pedal_threshold, first_note_at_zero)
|
|
227
227
|
|
|
228
228
|
performance = Performance(
|
|
229
|
-
id=get_document_name(filename),
|
|
230
|
-
performedparts=ppart,
|
|
229
|
+
id=get_document_name(filename), performedparts=ppart, ensure_unique_tracks=False
|
|
231
230
|
)
|
|
232
231
|
# Generate Part
|
|
233
232
|
if create_score:
|
|
@@ -345,10 +344,16 @@ def performed_part_from_match(
|
|
|
345
344
|
notes = list()
|
|
346
345
|
note_onsets_in_secs = np.array(np.zeros(len(mf.notes)), dtype=float)
|
|
347
346
|
note_onsets_in_tick = np.array(np.zeros(len(mf.notes)), dtype=int)
|
|
347
|
+
tracks = set()
|
|
348
|
+
channels = set()
|
|
348
349
|
for i, note in enumerate(mf.notes):
|
|
349
350
|
n_onset_sec = midi_ticks_to_seconds(note.Onset, mpq, ppq)
|
|
350
351
|
note_onsets_in_secs[i] = n_onset_sec
|
|
351
352
|
note_onsets_in_tick[i] = note.Onset
|
|
353
|
+
track = getattr(note, "Track", 0)
|
|
354
|
+
tracks.add(track)
|
|
355
|
+
channel = getattr(note, "Channel", 0)
|
|
356
|
+
channels.add(channel)
|
|
352
357
|
notes.append(
|
|
353
358
|
dict(
|
|
354
359
|
id=format_pnote_id(note.Id),
|
|
@@ -359,8 +364,8 @@ def performed_part_from_match(
|
|
|
359
364
|
note_off_tick=note.Offset,
|
|
360
365
|
sound_off=midi_ticks_to_seconds(note.Offset, mpq, ppq),
|
|
361
366
|
velocity=note.Velocity,
|
|
362
|
-
track=
|
|
363
|
-
channel=
|
|
367
|
+
track=track,
|
|
368
|
+
channel=channel,
|
|
364
369
|
)
|
|
365
370
|
)
|
|
366
371
|
# Set first note_on to zero in ticks and seconds if first_note_at_zero
|
|
@@ -375,12 +380,23 @@ def performed_part_from_match(
|
|
|
375
380
|
note["note_on_tick"] -= offset_tick
|
|
376
381
|
note["note_off_tick"] -= offset_tick
|
|
377
382
|
|
|
383
|
+
# check if multiple tracks are in the match file
|
|
384
|
+
if len(tracks) > 1:
|
|
385
|
+
warnings.warn("Notes on multiple MIDI tracks in matchfile" "information!.")
|
|
386
|
+
used_track = min(tracks)
|
|
387
|
+
# check if multiple tracks are in the match file
|
|
388
|
+
if len(channels) > 1:
|
|
389
|
+
warnings.warn("Notes on multiple MIDI channels in matchfile" "information!.")
|
|
390
|
+
used_channel = min(channels)
|
|
391
|
+
|
|
378
392
|
# SustainPedal instances for sustain pedal lines
|
|
379
393
|
sustain_pedal = [
|
|
380
394
|
dict(
|
|
381
395
|
number=64,
|
|
382
396
|
time=midi_ticks_to_seconds(ped.Time, mpq, ppq),
|
|
383
397
|
value=ped.Value,
|
|
398
|
+
track=used_track,
|
|
399
|
+
channel=used_channel,
|
|
384
400
|
)
|
|
385
401
|
for ped in mf.sustain_pedal
|
|
386
402
|
]
|
|
@@ -391,6 +407,8 @@ def performed_part_from_match(
|
|
|
391
407
|
number=67,
|
|
392
408
|
time=midi_ticks_to_seconds(ped.Time, mpq, ppq),
|
|
393
409
|
value=ped.Value,
|
|
410
|
+
track=used_track,
|
|
411
|
+
channel=used_channel,
|
|
394
412
|
)
|
|
395
413
|
for ped in mf.soft_pedal
|
|
396
414
|
]
|
|
@@ -402,6 +420,9 @@ def performed_part_from_match(
|
|
|
402
420
|
notes=notes,
|
|
403
421
|
controls=sustain_pedal + soft_pedal,
|
|
404
422
|
sustain_pedal_threshold=pedal_threshold,
|
|
423
|
+
ppq=ppq,
|
|
424
|
+
mpq=mpq,
|
|
425
|
+
track=used_track,
|
|
405
426
|
)
|
|
406
427
|
return ppart
|
|
407
428
|
|
|
@@ -458,9 +479,9 @@ def part_from_matchfile(
|
|
|
458
479
|
max_time = max(n.OffsetInBeats for n in snotes)
|
|
459
480
|
(
|
|
460
481
|
beats_map_from_beats,
|
|
461
|
-
|
|
482
|
+
beats_map_from_quarters,
|
|
462
483
|
beat_type_map_from_beats,
|
|
463
|
-
|
|
484
|
+
beat_type_map_from_quarters,
|
|
464
485
|
min_time_q,
|
|
465
486
|
max_time_q,
|
|
466
487
|
) = make_timesig_maps(ts, max_time)
|
|
@@ -468,13 +489,13 @@ def part_from_matchfile(
|
|
|
468
489
|
# compute necessary divs based on the types of notes in the
|
|
469
490
|
# match snotes (only integers)
|
|
470
491
|
divs_arg = [
|
|
471
|
-
max(int((
|
|
492
|
+
max(int((beat_type_map_from_beats(note.OnsetInBeats) / 4)), 1)
|
|
472
493
|
* note.Offset.denominator
|
|
473
494
|
* (note.Offset.tuple_div or 1)
|
|
474
495
|
for note in snotes
|
|
475
496
|
]
|
|
476
497
|
divs_arg += [
|
|
477
|
-
max(int((
|
|
498
|
+
max(int((beat_type_map_from_beats(note.OnsetInBeats) / 4)), 1)
|
|
478
499
|
* note.Duration.denominator
|
|
479
500
|
* (note.Duration.tuple_div or 1)
|
|
480
501
|
for note in snotes
|
|
@@ -484,24 +505,24 @@ def part_from_matchfile(
|
|
|
484
505
|
unique_onsets, inv_idxs = np.unique(onset_in_beats, return_inverse=True)
|
|
485
506
|
|
|
486
507
|
iois_in_beats = np.diff(unique_onsets)
|
|
487
|
-
beat_to_quarter = 4 /
|
|
508
|
+
beat_to_quarter = 4 / beat_type_map_from_beats(onset_in_beats)
|
|
488
509
|
|
|
489
510
|
iois_in_quarters_offset = np.r_[
|
|
490
511
|
beat_to_quarter[0] * onset_in_beats[0],
|
|
491
|
-
(4 /
|
|
512
|
+
(4 / beat_type_map_from_beats(unique_onsets[:-1])) * iois_in_beats,
|
|
492
513
|
]
|
|
493
514
|
onset_in_quarters = np.cumsum(iois_in_quarters_offset)
|
|
494
515
|
iois_in_quarters = np.diff(onset_in_quarters)
|
|
495
516
|
|
|
496
517
|
# ___ these divs are relative to quarters;
|
|
497
518
|
divs = np.lcm.reduce(np.unique(divs_arg))
|
|
498
|
-
onset_in_divs = np.r_[0, np.cumsum(divs * iois_in_quarters)][inv_idxs]
|
|
519
|
+
onset_in_divs = np.r_[0, np.cumsum(divs * iois_in_quarters, dtype=int)][inv_idxs]
|
|
499
520
|
onset_in_quarters = onset_in_quarters[inv_idxs]
|
|
500
521
|
|
|
501
522
|
part.set_quarter_duration(0, divs)
|
|
502
523
|
bars = np.unique([n.Measure for n in snotes])
|
|
503
524
|
t = min_time
|
|
504
|
-
t = t * 4 /
|
|
525
|
+
t = t * 4 / beat_type_map_from_beats(min_time)
|
|
505
526
|
offset = t
|
|
506
527
|
bar_times = {}
|
|
507
528
|
|
|
@@ -511,9 +532,9 @@ def part_from_matchfile(
|
|
|
511
532
|
# if starting beat is above zero, add padding
|
|
512
533
|
rest = score.Rest()
|
|
513
534
|
part.add(rest, start=0, end=t * divs)
|
|
514
|
-
onset_in_divs += t * divs
|
|
535
|
+
onset_in_divs += round(t * divs)
|
|
515
536
|
offset = 0
|
|
516
|
-
t = t - t %
|
|
537
|
+
t = t - t % beats_map_from_beats(min_time)
|
|
517
538
|
|
|
518
539
|
for b_name in bars:
|
|
519
540
|
notes_in_this_bar = [
|
|
@@ -552,14 +573,14 @@ def part_from_matchfile(
|
|
|
552
573
|
# on_off_scale = 1 means duration and beat offset are given in
|
|
553
574
|
# whole notes, else they're given in beats (as in the KAIST data)
|
|
554
575
|
if not match_offset_duration_in_whole:
|
|
555
|
-
on_off_scale =
|
|
576
|
+
on_off_scale = beat_type_map_from_quarters(bar_start)
|
|
556
577
|
|
|
557
578
|
# offset within bar in quarter units adjusted for different
|
|
558
|
-
# time signatures -> 4 /
|
|
559
|
-
bar_offset = (note.Beat - 1) * 4 /
|
|
579
|
+
# time signatures -> 4 / beat_type_map_from_quarters(bar_start)
|
|
580
|
+
bar_offset = (note.Beat - 1) * 4 / beat_type_map_from_quarters(bar_start)
|
|
560
581
|
|
|
561
582
|
# offset within beat in quarter units adjusted for different
|
|
562
|
-
# time signatures -> 4 /
|
|
583
|
+
# time signatures -> 4 / beat_type_map_from_quarters(bar_start)
|
|
563
584
|
beat_offset = (
|
|
564
585
|
4
|
|
565
586
|
/ on_off_scale
|
|
@@ -576,6 +597,7 @@ def part_from_matchfile(
|
|
|
576
597
|
"Calculated `onset_divs` does not match `OnsetInBeats` " "information!."
|
|
577
598
|
)
|
|
578
599
|
onset_divs = onset_in_divs[ni]
|
|
600
|
+
|
|
579
601
|
assert onset_divs >= 0
|
|
580
602
|
assert np.isclose(onset_divs, onset_in_divs[ni], atol=divs * 0.01)
|
|
581
603
|
is_tied = False
|
|
@@ -753,9 +775,9 @@ def part_from_matchfile(
|
|
|
753
775
|
last_closing_barline = barline_in_divs + int(
|
|
754
776
|
round(
|
|
755
777
|
divs
|
|
756
|
-
*
|
|
778
|
+
* beats_map_from_quarters(barline_in_quarters)
|
|
757
779
|
* 4
|
|
758
|
-
/
|
|
780
|
+
/ beat_type_map_from_quarters(barline_in_quarters)
|
|
759
781
|
)
|
|
760
782
|
)
|
|
761
783
|
part.add(prev_measure, None, last_closing_barline)
|
|
@@ -6,6 +6,7 @@ This module contains methods for importing MIDI files.
|
|
|
6
6
|
import warnings
|
|
7
7
|
|
|
8
8
|
from collections import defaultdict
|
|
9
|
+
from operator import itemgetter
|
|
9
10
|
from typing import Union, Optional, List, Tuple, Dict
|
|
10
11
|
import numpy as np
|
|
11
12
|
|
|
@@ -259,6 +260,7 @@ def load_performance_midi(
|
|
|
259
260
|
velocity=sounding_notes[note][2],
|
|
260
261
|
)
|
|
261
262
|
)
|
|
263
|
+
|
|
262
264
|
# remove hash from dict
|
|
263
265
|
del sounding_notes[note]
|
|
264
266
|
|
|
@@ -274,6 +276,25 @@ def load_performance_midi(
|
|
|
274
276
|
)
|
|
275
277
|
)
|
|
276
278
|
|
|
279
|
+
# adjust timing of events based on tempo changes
|
|
280
|
+
for note in notes:
|
|
281
|
+
note["note_on"] = adjust_time(note["note_on_tick"], tempo_changes, ppq)
|
|
282
|
+
note["note_off"] = adjust_time(note["note_off_tick"], tempo_changes, ppq)
|
|
283
|
+
for control in controls:
|
|
284
|
+
control["time"] = adjust_time(control["time_tick"], tempo_changes, ppq)
|
|
285
|
+
for program in programs:
|
|
286
|
+
program["time"] = adjust_time(program["time_tick"], tempo_changes, ppq)
|
|
287
|
+
for time_signature in time_signatures:
|
|
288
|
+
time_signature["time"] = adjust_time(
|
|
289
|
+
time_signature["time_tick"], tempo_changes, ppq
|
|
290
|
+
)
|
|
291
|
+
for key_signature in key_signatures:
|
|
292
|
+
key_signature["time"] = adjust_time(
|
|
293
|
+
key_signature["time_tick"], tempo_changes, ppq
|
|
294
|
+
)
|
|
295
|
+
for meta in meta_other:
|
|
296
|
+
meta["time"] = adjust_time(meta["time_tick"], tempo_changes, ppq)
|
|
297
|
+
|
|
277
298
|
# add note id to every note
|
|
278
299
|
for k, note in enumerate(notes):
|
|
279
300
|
note["id"] = f"n{k}"
|
|
@@ -290,33 +311,13 @@ def load_performance_midi(
|
|
|
290
311
|
mpq=default_mpq,
|
|
291
312
|
track=i,
|
|
292
313
|
)
|
|
293
|
-
|
|
294
314
|
pps.append(pp)
|
|
295
315
|
|
|
296
|
-
# adjust timing of events based on tempo changes
|
|
297
|
-
for pp in pps:
|
|
298
|
-
for note in pp.notes:
|
|
299
|
-
note["note_on"] = adjust_time(note["note_on_tick"], tempo_changes, ppq)
|
|
300
|
-
note["note_off"] = adjust_time(note["note_off_tick"], tempo_changes, ppq)
|
|
301
|
-
for control in pp.controls:
|
|
302
|
-
control["time"] = adjust_time(control["time_tick"], tempo_changes, ppq)
|
|
303
|
-
for program in pp.programs:
|
|
304
|
-
program["time"] = adjust_time(program["time_tick"], tempo_changes, ppq)
|
|
305
|
-
for time_signature in pp.time_signatures:
|
|
306
|
-
time_signature["time"] = adjust_time(
|
|
307
|
-
time_signature["time_tick"], tempo_changes, ppq
|
|
308
|
-
)
|
|
309
|
-
for key_signature in pp.key_signatures:
|
|
310
|
-
key_signature["time"] = adjust_time(
|
|
311
|
-
key_signature["time_tick"], tempo_changes, ppq
|
|
312
|
-
)
|
|
313
|
-
for meta in pp.meta_other:
|
|
314
|
-
meta["time"] = adjust_time(meta["time_tick"], tempo_changes, ppq)
|
|
315
|
-
|
|
316
316
|
perf = performance.Performance(
|
|
317
317
|
id=doc_name,
|
|
318
318
|
performedparts=pps,
|
|
319
319
|
)
|
|
320
|
+
|
|
320
321
|
return perf
|
|
321
322
|
|
|
322
323
|
|
|
@@ -618,23 +619,25 @@ or a list of these
|
|
|
618
619
|
# now clear all track_ts
|
|
619
620
|
time_sigs_by_track.clear()
|
|
620
621
|
|
|
621
|
-
|
|
622
|
+
# use a list to preserve all time sigs in order
|
|
623
|
+
time_sigs_by_part = defaultdict(list)
|
|
622
624
|
for tr, ts_list in time_sigs_by_track.items():
|
|
623
625
|
for ts in ts_list:
|
|
624
626
|
for part in track_to_part_mapping[tr]:
|
|
625
|
-
time_sigs_by_part[part].
|
|
627
|
+
time_sigs_by_part[part].append(ts)
|
|
626
628
|
for ts in global_time_sigs:
|
|
627
629
|
for part in set(part for _, part, _ in group_part_voice_keys):
|
|
628
|
-
time_sigs_by_part[part].
|
|
630
|
+
time_sigs_by_part[part].append(ts)
|
|
629
631
|
|
|
630
|
-
|
|
632
|
+
# use a list to preserve all key sigs in order
|
|
633
|
+
key_sigs_by_part = defaultdict(list)
|
|
631
634
|
for tr, ks_list in key_sigs_by_track.items():
|
|
632
635
|
for ks in ks_list:
|
|
633
636
|
for part in track_to_part_mapping[tr]:
|
|
634
|
-
key_sigs_by_part[part].
|
|
637
|
+
key_sigs_by_part[part].append(ks)
|
|
635
638
|
for ks in global_key_sigs:
|
|
636
639
|
for part in set(part for _, part, _ in group_part_voice_keys):
|
|
637
|
-
key_sigs_by_part[part].
|
|
640
|
+
key_sigs_by_part[part].append(ks)
|
|
638
641
|
|
|
639
642
|
# names_by_part = defaultdict(set)
|
|
640
643
|
# for tr_ch, pg_p_v in zip(tr_ch_keys, group_part_voice_keys):
|
|
@@ -661,13 +664,11 @@ or a list of these
|
|
|
661
664
|
spellings,
|
|
662
665
|
voices,
|
|
663
666
|
note_ids,
|
|
664
|
-
sorted(time_sigs_by_part[part_nr]),
|
|
665
|
-
sorted(key_sigs_by_part[part_nr]),
|
|
667
|
+
sorted(time_sigs_by_part[part_nr], key=itemgetter(0)),
|
|
668
|
+
sorted(key_sigs_by_part[part_nr], key=itemgetter(0)),
|
|
666
669
|
part_id="P{}".format(part_nr + 1),
|
|
667
670
|
part_name=part_names.get(part_nr, None),
|
|
668
671
|
)
|
|
669
|
-
|
|
670
|
-
# print(part.pretty())
|
|
671
672
|
# if this part has an associated part_group number we create a PartGroup
|
|
672
673
|
# if necessary, and add the part to that. The newly created PartGroup is
|
|
673
674
|
# then added to the partlist.
|