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
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
from partitura.score import ScoreLike, Part
|
|
2
|
-
from partitura.utils import (
|
|
2
|
+
from partitura.utils import (
|
|
3
|
+
estimate_symbolic_duration,
|
|
4
|
+
estimate_clef_properties,
|
|
5
|
+
key_name_to_fifths_mode,
|
|
6
|
+
fifths_mode_to_key_name,
|
|
7
|
+
)
|
|
3
8
|
import warnings
|
|
4
9
|
import numpy as np
|
|
5
10
|
from typing import Union
|
|
@@ -12,14 +17,14 @@ import partitura.score as score
|
|
|
12
17
|
def create_divs_from_beats(note_array: np.ndarray):
|
|
13
18
|
"""
|
|
14
19
|
Append onset_div and duration_div fields to the note array.
|
|
15
|
-
Assumes beats are in uniform units across the whole array
|
|
20
|
+
Assumes beats are in uniform units across the whole array
|
|
16
21
|
(no time signature change that modifies beat unit, e.g., 4/4 to 6/8).
|
|
17
22
|
|
|
18
23
|
This function may result in an error if time signature changes that affect the ratio of beat/div are present.
|
|
19
24
|
Parameters
|
|
20
25
|
----------
|
|
21
26
|
note_array: np.ndarray
|
|
22
|
-
The note array to which the divs fields will be added.
|
|
27
|
+
The note array to which the divs fields will be added.
|
|
23
28
|
Normally only beat onset and duration are provided.
|
|
24
29
|
|
|
25
30
|
Returns
|
|
@@ -28,18 +33,33 @@ def create_divs_from_beats(note_array: np.ndarray):
|
|
|
28
33
|
The note array with the divs fields added.
|
|
29
34
|
divs: int
|
|
30
35
|
the divs per beat
|
|
31
|
-
|
|
36
|
+
|
|
32
37
|
"""
|
|
33
|
-
duration_fractions = [
|
|
34
|
-
|
|
38
|
+
duration_fractions = [
|
|
39
|
+
Fraction(float(ix)).limit_denominator(256) for ix in note_array["duration_beat"]
|
|
40
|
+
]
|
|
41
|
+
onset_fractions = [
|
|
42
|
+
Fraction(float(ix)).limit_denominator(256) for ix in note_array["onset_beat"]
|
|
43
|
+
]
|
|
35
44
|
divs = np.lcm.reduce(
|
|
36
|
-
[
|
|
37
|
-
|
|
45
|
+
[
|
|
46
|
+
Fraction(float(ix)).limit_denominator(256).denominator
|
|
47
|
+
for ix in np.unique(note_array["duration_beat"])
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
onset_divs = list(
|
|
51
|
+
map(lambda r: int(divs * r.numerator / r.denominator), onset_fractions)
|
|
52
|
+
)
|
|
38
53
|
min_onset_div = min(onset_divs)
|
|
39
54
|
if min_onset_div < 0:
|
|
40
55
|
onset_divs = list(map(lambda x: x - min_onset_div, onset_divs))
|
|
41
|
-
duration_divs = list(
|
|
42
|
-
|
|
56
|
+
duration_divs = list(
|
|
57
|
+
map(lambda r: int(divs * r.numerator / r.denominator), duration_fractions)
|
|
58
|
+
)
|
|
59
|
+
na_divs = np.array(
|
|
60
|
+
list(zip(onset_divs, duration_divs)),
|
|
61
|
+
dtype=[("onset_div", int), ("duration_div", int)],
|
|
62
|
+
)
|
|
43
63
|
return rfn.merge_arrays((note_array, na_divs), flatten=True, usemask=False), divs
|
|
44
64
|
|
|
45
65
|
|
|
@@ -51,7 +71,7 @@ def create_beats_from_divs(note_array: np.ndarray, divs: int):
|
|
|
51
71
|
Parameters
|
|
52
72
|
----------
|
|
53
73
|
note_array: np.ndarray
|
|
54
|
-
The note array to which the divs fields will be added.
|
|
74
|
+
The note array to which the divs fields will be added.
|
|
55
75
|
Normally only beat onset and duration are provided.
|
|
56
76
|
divs: int
|
|
57
77
|
Divs/ticks per quarter note.
|
|
@@ -60,24 +80,27 @@ def create_beats_from_divs(note_array: np.ndarray, divs: int):
|
|
|
60
80
|
-------
|
|
61
81
|
note_array: np.ndarray
|
|
62
82
|
The note array with the divs fields added.
|
|
63
|
-
|
|
83
|
+
|
|
64
84
|
"""
|
|
65
|
-
onset_beats = list(note_array["onset_div"]/divs)
|
|
66
|
-
duration_beats = list(note_array["duration_div"]/divs)
|
|
67
|
-
na_beats = np.array(
|
|
85
|
+
onset_beats = list(note_array["onset_div"] / divs)
|
|
86
|
+
duration_beats = list(note_array["duration_div"] / divs)
|
|
87
|
+
na_beats = np.array(
|
|
88
|
+
list(zip(onset_beats, duration_beats)),
|
|
89
|
+
dtype=[("onset_beat", float), ("duration_beat", float)],
|
|
90
|
+
)
|
|
68
91
|
return rfn.merge_arrays((note_array, na_beats), flatten=True, usemask=False)
|
|
69
92
|
|
|
70
93
|
|
|
71
94
|
def create_part(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
ticks: int,
|
|
96
|
+
note_array: np.ndarray,
|
|
97
|
+
key_sigs: list = None,
|
|
98
|
+
time_sigs: list = None,
|
|
99
|
+
part_id: str = None,
|
|
100
|
+
part_name: str = None,
|
|
101
|
+
sanitize: bool = True,
|
|
102
|
+
anacrusis_divs: int = 0,
|
|
103
|
+
barebones: bool = False,
|
|
81
104
|
):
|
|
82
105
|
"""
|
|
83
106
|
Create a part from a note array and a list of key signatures.
|
|
@@ -109,15 +132,15 @@ def create_part(
|
|
|
109
132
|
The part created from the note array and key signatures.
|
|
110
133
|
"""
|
|
111
134
|
|
|
112
|
-
|
|
113
135
|
warnings.warn("create_part", stacklevel=2)
|
|
114
136
|
|
|
115
|
-
part = Part(
|
|
137
|
+
part = Part(
|
|
138
|
+
part_id,
|
|
139
|
+
part_name=part_name,
|
|
140
|
+
)
|
|
116
141
|
part.set_quarter_duration(0, ticks)
|
|
117
142
|
|
|
118
|
-
clef = score.Clef(
|
|
119
|
-
staff=1, **estimate_clef_properties(note_array["pitch"])
|
|
120
|
-
)
|
|
143
|
+
clef = score.Clef(staff=1, **estimate_clef_properties(note_array["pitch"]))
|
|
121
144
|
part.add(clef, 0)
|
|
122
145
|
|
|
123
146
|
# key sig
|
|
@@ -128,7 +151,7 @@ def create_part(
|
|
|
128
151
|
part.add(score.KeySignature(fifths, mode), t_start, t_end)
|
|
129
152
|
else:
|
|
130
153
|
warnings.warn("No key signatures added")
|
|
131
|
-
|
|
154
|
+
|
|
132
155
|
# time sig
|
|
133
156
|
if time_sigs is not None:
|
|
134
157
|
for ts_start, num, den, ts_end in time_sigs:
|
|
@@ -149,7 +172,7 @@ def create_part(
|
|
|
149
172
|
alter=n["alter"],
|
|
150
173
|
voice=int(n["voice"] or 0),
|
|
151
174
|
id=n["id"],
|
|
152
|
-
symbolic_duration=estimate_symbolic_duration(n["duration_div"], ticks)
|
|
175
|
+
symbolic_duration=estimate_symbolic_duration(n["duration_div"], ticks),
|
|
153
176
|
)
|
|
154
177
|
else:
|
|
155
178
|
note = score.GraceNote(
|
|
@@ -190,17 +213,18 @@ def create_part(
|
|
|
190
213
|
|
|
191
214
|
|
|
192
215
|
def note_array_to_score(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
216
|
+
note_array: Union[np.ndarray, list],
|
|
217
|
+
name_id: str = "",
|
|
218
|
+
divs: int = None,
|
|
219
|
+
key_sigs: list = None,
|
|
220
|
+
time_sigs: list = None,
|
|
221
|
+
part_name: str = "",
|
|
222
|
+
assign_note_ids: bool = True,
|
|
223
|
+
estimate_key: bool = False,
|
|
224
|
+
estimate_time: bool = False,
|
|
225
|
+
sanitize: bool = True,
|
|
226
|
+
return_part: bool = False,
|
|
227
|
+
) -> ScoreLike:
|
|
204
228
|
"""
|
|
205
229
|
A generic function to transform an enriched note_array to part or Score.
|
|
206
230
|
|
|
@@ -263,7 +287,7 @@ def note_array_to_score(
|
|
|
263
287
|
- key_fifths(optional)
|
|
264
288
|
- id (optional)
|
|
265
289
|
divs : int (optional)
|
|
266
|
-
Divs/ticks per quarter note.
|
|
290
|
+
Divs/ticks per quarter note.
|
|
267
291
|
If not given, it is estimated assuming a beats in quarters.
|
|
268
292
|
key_sigs: list (optional)
|
|
269
293
|
A list of key signatures. Each key signature is a tuple of the form (onset, key_name, offset).
|
|
@@ -292,10 +316,18 @@ def note_array_to_score(
|
|
|
292
316
|
|
|
293
317
|
if isinstance(note_array, list):
|
|
294
318
|
parts = [
|
|
295
|
-
note_array_to_score(
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
319
|
+
note_array_to_score(
|
|
320
|
+
note_array=x,
|
|
321
|
+
name_id=str(i),
|
|
322
|
+
assign_note_ids=assign_note_ids,
|
|
323
|
+
return_part=True,
|
|
324
|
+
divs=divs,
|
|
325
|
+
estimate_key=estimate_key,
|
|
326
|
+
sanitize=sanitize,
|
|
327
|
+
part_name=name_id + "_P" + str(i),
|
|
328
|
+
)
|
|
329
|
+
for i, x in enumerate(note_array)
|
|
330
|
+
]
|
|
299
331
|
return score.Score(partlist=parts)
|
|
300
332
|
|
|
301
333
|
# Input validation
|
|
@@ -311,14 +343,14 @@ def note_array_to_score(
|
|
|
311
343
|
ks_case = ["key_fifths", "key_mode"]
|
|
312
344
|
|
|
313
345
|
case1 = ["onset_beat", "duration_beat", "pitch"]
|
|
314
|
-
case1_ex = ["onset_div", "duration_div"]
|
|
346
|
+
case1_ex = ["onset_div", "duration_div"]
|
|
315
347
|
case2 = ["onset_div", "duration_div", "pitch"]
|
|
316
|
-
case2_ex = ["onset_beat", "duration_beat"]
|
|
348
|
+
case2_ex = ["onset_beat", "duration_beat"]
|
|
317
349
|
# case3 = ["onset_div", "duration_div", "onset_beat", "duration_beat", "pitch"]
|
|
318
350
|
|
|
319
351
|
if not (all([x in dtypes for x in case1]) or all([x in dtypes for x in case2])):
|
|
320
352
|
raise ValueError("not all necessary note array fields are available")
|
|
321
|
-
|
|
353
|
+
|
|
322
354
|
# sort the array
|
|
323
355
|
onset_time = "onset_div"
|
|
324
356
|
duration_time = "duration_div"
|
|
@@ -327,7 +359,9 @@ def note_array_to_score(
|
|
|
327
359
|
duration_time = "duration_beat"
|
|
328
360
|
|
|
329
361
|
# Order Lexicographically
|
|
330
|
-
sort_idx = np.lexsort(
|
|
362
|
+
sort_idx = np.lexsort(
|
|
363
|
+
(note_array[duration_time], note_array["pitch"], note_array[onset_time])
|
|
364
|
+
)
|
|
331
365
|
note_array = note_array[sort_idx]
|
|
332
366
|
|
|
333
367
|
# case 1, estimate divs
|
|
@@ -336,37 +370,37 @@ def note_array_to_score(
|
|
|
336
370
|
note_array, divs_ = create_divs_from_beats(note_array)
|
|
337
371
|
if divs is not None and divs != divs_:
|
|
338
372
|
raise ValueError("estimated divs don't correspond to input divs")
|
|
339
|
-
else:
|
|
373
|
+
else:
|
|
340
374
|
divs = divs_
|
|
341
375
|
|
|
342
|
-
# case 1: convert key sig times to divs
|
|
376
|
+
# case 1: convert key sig times to divs
|
|
343
377
|
if key_sigs is not None:
|
|
344
378
|
key_sigs = np.array(key_sigs)
|
|
345
379
|
if key_sigs.shape[1] == 2:
|
|
346
|
-
key_sigs[:,0] = (key_sigs[:,0] / divs).astype(int)
|
|
380
|
+
key_sigs[:, 0] = (key_sigs[:, 0] / divs).astype(int)
|
|
347
381
|
elif key_sigs.shape[1] == 3:
|
|
348
|
-
key_sigs[:,0] = (key_sigs[:,0] / divs).astype(int)
|
|
349
|
-
key_sigs[:,2] = (key_sigs[:,2] / divs).astype(int)
|
|
382
|
+
key_sigs[:, 0] = (key_sigs[:, 0] / divs).astype(int)
|
|
383
|
+
key_sigs[:, 2] = (key_sigs[:, 2] / divs).astype(int)
|
|
350
384
|
else:
|
|
351
385
|
raise ValueError("key_sigs is given in a wrong format")
|
|
352
|
-
|
|
353
|
-
# case 1: convert time sig times to divs
|
|
386
|
+
|
|
387
|
+
# case 1: convert time sig times to divs
|
|
354
388
|
if time_sigs is not None:
|
|
355
389
|
time_sigs = np.array(time_sigs)
|
|
356
390
|
if time_sigs.shape[1] == 3:
|
|
357
|
-
time_sigs[:,0] = (time_sigs[:,0] / divs).astype(int)
|
|
391
|
+
time_sigs[:, 0] = (time_sigs[:, 0] / divs).astype(int)
|
|
358
392
|
elif time_sigs.shape[1] == 4:
|
|
359
|
-
time_sigs[:,0] = (time_sigs[:,0] / divs).astype(int)
|
|
360
|
-
time_sigs[:,3] = (time_sigs[:,3] / divs).astype(int)
|
|
393
|
+
time_sigs[:, 0] = (time_sigs[:, 0] / divs).astype(int)
|
|
394
|
+
time_sigs[:, 3] = (time_sigs[:, 3] / divs).astype(int)
|
|
361
395
|
else:
|
|
362
396
|
raise ValueError("time_sigs is given in a wrong format")
|
|
363
397
|
|
|
364
398
|
# case 2, estimate beats
|
|
365
399
|
if all([x in dtypes for x in case2] and [x not in dtypes for x in case2_ex]):
|
|
366
400
|
# estimate onset_beats and duration_beats in quarters
|
|
367
|
-
if divs is None
|
|
401
|
+
if divs is None:
|
|
368
402
|
raise ValueError("Divs/ticks need to be specified")
|
|
369
|
-
else:
|
|
403
|
+
else:
|
|
370
404
|
note_array = create_beats_from_divs(note_array, divs)
|
|
371
405
|
|
|
372
406
|
if divs is None:
|
|
@@ -375,40 +409,50 @@ def note_array_to_score(
|
|
|
375
409
|
if dur != 0:
|
|
376
410
|
break
|
|
377
411
|
if all([x in dtypes for x in ts_case]):
|
|
378
|
-
divs = int(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
412
|
+
divs = int(
|
|
413
|
+
(note_array[idx]["duration_div"] / note_array[idx]["duration_beat"])
|
|
414
|
+
/ (4 / note_array[idx]["ts_beat_type"])
|
|
415
|
+
)
|
|
416
|
+
else:
|
|
417
|
+
divs = int(
|
|
418
|
+
note_array[idx]["duration_div"] / note_array[idx]["duration_beat"]
|
|
419
|
+
)
|
|
382
420
|
|
|
383
421
|
# Test Note array for negative durations
|
|
384
422
|
if not np.all(note_array["duration_div"] >= 0):
|
|
385
423
|
raise ValueError("Note array contains negative durations.")
|
|
386
424
|
if not np.all(note_array["duration_beat"] >= 0):
|
|
387
425
|
raise ValueError("Note array contains negative durations.")
|
|
388
|
-
|
|
426
|
+
|
|
389
427
|
# Test for negative divs
|
|
390
428
|
if not np.all(note_array["onset_div"] >= 0):
|
|
391
429
|
raise ValueError("Negative divs found in note_array.")
|
|
392
|
-
|
|
430
|
+
|
|
393
431
|
# handle time signatures
|
|
394
|
-
if all([x in dtypes for x in ts_case]):
|
|
432
|
+
if all([x in dtypes for x in ts_case]):
|
|
395
433
|
time_sigs = [[0, note_array[0]["ts_beats"], note_array[0]["ts_beat_type"]]]
|
|
396
434
|
for n in note_array:
|
|
397
|
-
if
|
|
435
|
+
if (
|
|
436
|
+
n["ts_beats"] != time_sigs[-1][1]
|
|
437
|
+
or n["ts_beat_type"] != time_sigs[-1][2]
|
|
438
|
+
):
|
|
398
439
|
time_sigs.append([n["onset_div"], n["ts_beats"], n["ts_beat_type"]])
|
|
399
440
|
global_time_sigs = np.array(time_sigs)
|
|
400
441
|
elif time_sigs is not None:
|
|
401
442
|
global_time_sigs = time_sigs
|
|
402
443
|
elif estimate_time:
|
|
403
444
|
global_time_sigs = [[0, 4, 4]]
|
|
404
|
-
else:
|
|
445
|
+
else:
|
|
405
446
|
global_time_sigs = None
|
|
406
447
|
|
|
407
448
|
if global_time_sigs is not None:
|
|
408
449
|
global_time_sigs = np.array(global_time_sigs)
|
|
409
450
|
if global_time_sigs.shape[1] == 3:
|
|
410
451
|
# for convenience, we add the end times for each time signature
|
|
411
|
-
ts_end_times = np.r_[
|
|
452
|
+
ts_end_times = np.r_[
|
|
453
|
+
global_time_sigs[1:, 0],
|
|
454
|
+
np.max(note_array["onset_div"] + note_array["duration_div"]),
|
|
455
|
+
]
|
|
412
456
|
global_time_sigs = np.column_stack((global_time_sigs, ts_end_times))
|
|
413
457
|
elif global_time_sigs.shape[1] == 4:
|
|
414
458
|
pass
|
|
@@ -417,19 +461,17 @@ def note_array_to_score(
|
|
|
417
461
|
|
|
418
462
|
# make sure there is a time signature from the beginning
|
|
419
463
|
global_time_sigs[0, 0] = 0
|
|
420
|
-
|
|
421
|
-
|
|
422
464
|
|
|
423
465
|
# Note id creation or re-assignment
|
|
424
466
|
if "id" not in dtypes:
|
|
425
467
|
note_ids = ["{}n{:4d}".format(name_id, i) for i in range(len(note_array))]
|
|
426
|
-
note_array = rfn.append_fields(
|
|
468
|
+
note_array = rfn.append_fields(
|
|
469
|
+
note_array, "id", np.array(note_ids, dtype="<U256")
|
|
470
|
+
)
|
|
427
471
|
elif assign_note_ids or np.all(note_array["id"] == note_array["id"][0]):
|
|
428
472
|
note_ids = ["{}n{:4d}".format(name_id, i) for i in range(len(note_array))]
|
|
429
473
|
note_array["id"] = np.array(note_ids)
|
|
430
474
|
|
|
431
|
-
|
|
432
|
-
|
|
433
475
|
# estimate voice
|
|
434
476
|
if "voice" in dtypes:
|
|
435
477
|
estimate_voice_info = False
|
|
@@ -444,23 +486,36 @@ def note_array_to_score(
|
|
|
444
486
|
# Zero duration notes are currently deleted
|
|
445
487
|
estimated_voices = analysis.estimate_voices(note_array)
|
|
446
488
|
assert len(part_voice_list) == len(estimated_voices)
|
|
447
|
-
for i, (part_voice, voice_est) in enumerate(
|
|
489
|
+
for i, (part_voice, voice_est) in enumerate(
|
|
490
|
+
zip(part_voice_list, estimated_voices)
|
|
491
|
+
):
|
|
448
492
|
# Not sure if correct.
|
|
449
493
|
if part_voice != np.inf:
|
|
450
494
|
estimated_voices[i] = part_voice
|
|
451
|
-
note_array = rfn.append_fields(
|
|
495
|
+
note_array = rfn.append_fields(
|
|
496
|
+
note_array, "voice", np.array(estimated_voices, dtype=int)
|
|
497
|
+
)
|
|
452
498
|
|
|
453
499
|
# estimate pitch spelling
|
|
454
|
-
if not all(x in dtypes for x in [
|
|
500
|
+
if not all(x in dtypes for x in ["step", "alter", "octave"]):
|
|
455
501
|
warnings.warn("pitch spelling")
|
|
456
502
|
spelling_global = analysis.estimate_spelling(note_array)
|
|
457
503
|
note_array = rfn.merge_arrays((note_array, spelling_global), flatten=True)
|
|
458
504
|
|
|
459
505
|
# handle or estimate key signature
|
|
460
|
-
if all([x in dtypes for x in ks_case]):
|
|
461
|
-
global_key_sigs = [
|
|
506
|
+
if all([x in dtypes for x in ks_case]):
|
|
507
|
+
global_key_sigs = [
|
|
508
|
+
[
|
|
509
|
+
0,
|
|
510
|
+
fifths_mode_to_key_name(
|
|
511
|
+
note_array[0]["ks_fifths"], note_array[0]["ks_mode"]
|
|
512
|
+
),
|
|
513
|
+
]
|
|
514
|
+
]
|
|
462
515
|
for n in note_array:
|
|
463
|
-
global_key_sigs.append(
|
|
516
|
+
global_key_sigs.append(
|
|
517
|
+
[n["onset_div"], fifths_mode_to_key_name(n["ks_fifths"], n["ks_mode"])]
|
|
518
|
+
)
|
|
464
519
|
else:
|
|
465
520
|
global_key_sigs = key_sigs
|
|
466
521
|
elif key_sigs is not None:
|
|
@@ -475,16 +530,19 @@ def note_array_to_score(
|
|
|
475
530
|
global_key_sigs = np.array(global_key_sigs)
|
|
476
531
|
if global_key_sigs.shape[1] == 2:
|
|
477
532
|
# for convenience, we add the end times for each time signature
|
|
478
|
-
ks_end_times = np.r_[
|
|
533
|
+
ks_end_times = np.r_[
|
|
534
|
+
global_key_sigs[1:, 0],
|
|
535
|
+
np.max(note_array["onset_div"] + note_array["duration_div"]),
|
|
536
|
+
]
|
|
479
537
|
global_key_sigs = np.column_stack((global_key_sigs, ks_end_times))
|
|
480
538
|
elif global_key_sigs.shape[1] == 3:
|
|
481
539
|
pass
|
|
482
540
|
else:
|
|
483
541
|
raise ValueError("key_sigs is given in a wrong format")
|
|
484
|
-
|
|
542
|
+
|
|
485
543
|
# make sure there is a key signature from the beginning
|
|
486
544
|
global_key_sigs[0, 0] = 0
|
|
487
|
-
|
|
545
|
+
|
|
488
546
|
# Steps for dealing with anacrusis measure.
|
|
489
547
|
anacrusis_mask = np.zeros(len(note_array), dtype=bool)
|
|
490
548
|
anacrusis_mask[note_array["onset_beat"] < 0] = True
|
|
@@ -494,15 +552,13 @@ def note_array_to_score(
|
|
|
494
552
|
else:
|
|
495
553
|
last_neg_beat = np.max(note_array[anacrusis_mask]["onset_beat"])
|
|
496
554
|
last_neg_divs = np.max(note_array[anacrusis_mask]["onset_div"])
|
|
497
|
-
if all([x in dtypes for x in ts_case]):
|
|
555
|
+
if all([x in dtypes for x in ts_case]):
|
|
498
556
|
beat_type = np.max(note_array[anacrusis_mask]["ts_beat_type"])
|
|
499
557
|
else:
|
|
500
558
|
beat_type = 4
|
|
501
559
|
difference_from_zero = (0 - last_neg_beat) * divs * (4 / beat_type)
|
|
502
560
|
anacrusis_divs = int(last_neg_divs + difference_from_zero)
|
|
503
561
|
|
|
504
|
-
|
|
505
|
-
|
|
506
562
|
# Create the part
|
|
507
563
|
part = create_part(
|
|
508
564
|
ticks=divs,
|
|
@@ -512,11 +568,10 @@ def note_array_to_score(
|
|
|
512
568
|
part_id=name_id,
|
|
513
569
|
part_name=part_name,
|
|
514
570
|
sanitize=sanitize,
|
|
515
|
-
anacrusis_divs=anacrusis_divs
|
|
571
|
+
anacrusis_divs=anacrusis_divs,
|
|
516
572
|
)
|
|
517
573
|
# Return Part or Score
|
|
518
574
|
if return_part:
|
|
519
575
|
return part
|
|
520
576
|
else:
|
|
521
577
|
return score.Score(partlist=[part], id=name_id)
|
|
522
|
-
|