Spaces:
Running
on
Zero
Running
on
Zero
Upload TMIDIX.py
Browse files
TMIDIX.py
CHANGED
@@ -14,14 +14,14 @@ r'''############################################################################
|
|
14 |
#
|
15 |
# Project Los Angeles
|
16 |
#
|
17 |
-
# Tegridy Code
|
18 |
#
|
19 |
# https://github.com/Tegridy-Code/Project-Los-Angeles
|
20 |
#
|
21 |
#
|
22 |
###################################################################################
|
23 |
###################################################################################
|
24 |
-
# Copyright
|
25 |
#
|
26 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
27 |
# you may not use this file except in compliance with the License.
|
@@ -4690,7 +4690,8 @@ def augment_enhanced_score_notes(enhanced_score_notes,
|
|
4690 |
pitch_shift=0,
|
4691 |
ceil_timings=False,
|
4692 |
round_timings=False,
|
4693 |
-
legacy_timings=True
|
|
|
4694 |
):
|
4695 |
|
4696 |
esn = copy.deepcopy(enhanced_score_notes)
|
@@ -4740,6 +4741,9 @@ def augment_enhanced_score_notes(enhanced_score_notes,
|
|
4740 |
esn.sort(key=lambda x: x[6])
|
4741 |
esn.sort(key=lambda x: x[4], reverse=True)
|
4742 |
esn.sort(key=lambda x: x[1])
|
|
|
|
|
|
|
4743 |
|
4744 |
return esn
|
4745 |
|
@@ -7097,13 +7101,25 @@ def escore_notes_averages(escore_notes,
|
|
7097 |
else:
|
7098 |
durs = [e[durs_index] for e in escore_notes if e[chans_index] != 9]
|
7099 |
|
|
|
|
|
|
|
|
|
|
|
|
|
7100 |
if return_ptcs_and_vels:
|
7101 |
if average_drums:
|
7102 |
ptcs = [e[ptcs_index] for e in escore_notes]
|
7103 |
vels = [e[vels_index] for e in escore_notes]
|
7104 |
else:
|
7105 |
ptcs = [e[ptcs_index] for e in escore_notes if e[chans_index] != 9]
|
7106 |
-
vels = [e[vels_index] for e in escore_notes if e[chans_index] != 9]
|
|
|
|
|
|
|
|
|
|
|
|
|
7107 |
|
7108 |
return [sum(times) / len(times), sum(durs) / len(durs), sum(ptcs) / len(ptcs), sum(vels) / len(vels)]
|
7109 |
|
@@ -7883,19 +7899,21 @@ def solo_piano_escore_notes(escore_notes,
|
|
7883 |
chord = []
|
7884 |
|
7885 |
for cc in c:
|
7886 |
-
if cc[pitches_index] not in seen:
|
7887 |
|
7888 |
-
|
|
|
|
|
7889 |
cc[channels_index] = 0
|
7890 |
cc[patches_index] = 0
|
7891 |
-
|
7892 |
chord.append(cc)
|
7893 |
seen.append(cc[pitches_index])
|
7894 |
-
|
7895 |
-
|
7896 |
-
|
|
|
7897 |
chord.append(cc)
|
7898 |
-
seen.append(cc[pitches_index])
|
7899 |
|
7900 |
sp_escore_notes.append(chord)
|
7901 |
|
@@ -9254,6 +9272,1032 @@ def find_highest_density_escore_notes_chunk(escore_notes, max_chunk_time=512):
|
|
9254 |
|
9255 |
return chunk_escore
|
9256 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9257 |
###################################################################################
|
9258 |
#
|
9259 |
# This is the end of the TMIDI X Python module
|
|
|
14 |
#
|
15 |
# Project Los Angeles
|
16 |
#
|
17 |
+
# Tegridy Code 2024
|
18 |
#
|
19 |
# https://github.com/Tegridy-Code/Project-Los-Angeles
|
20 |
#
|
21 |
#
|
22 |
###################################################################################
|
23 |
###################################################################################
|
24 |
+
# Copyright 2024 Project Los Angeles / Tegridy Code
|
25 |
#
|
26 |
# Licensed under the Apache License, Version 2.0 (the "License");
|
27 |
# you may not use this file except in compliance with the License.
|
|
|
4690 |
pitch_shift=0,
|
4691 |
ceil_timings=False,
|
4692 |
round_timings=False,
|
4693 |
+
legacy_timings=True,
|
4694 |
+
sort_drums_last=False
|
4695 |
):
|
4696 |
|
4697 |
esn = copy.deepcopy(enhanced_score_notes)
|
|
|
4741 |
esn.sort(key=lambda x: x[6])
|
4742 |
esn.sort(key=lambda x: x[4], reverse=True)
|
4743 |
esn.sort(key=lambda x: x[1])
|
4744 |
+
|
4745 |
+
if sort_drums_last:
|
4746 |
+
esn.sort(key=lambda x: (x[1], -x[4], x[6]) if x[6] != 128 else (x[1], x[6], -x[4]))
|
4747 |
|
4748 |
return esn
|
4749 |
|
|
|
7101 |
else:
|
7102 |
durs = [e[durs_index] for e in escore_notes if e[chans_index] != 9]
|
7103 |
|
7104 |
+
if len(times) == 0:
|
7105 |
+
times = [0]
|
7106 |
+
|
7107 |
+
if len(durs) == 0:
|
7108 |
+
durs = [0]
|
7109 |
+
|
7110 |
if return_ptcs_and_vels:
|
7111 |
if average_drums:
|
7112 |
ptcs = [e[ptcs_index] for e in escore_notes]
|
7113 |
vels = [e[vels_index] for e in escore_notes]
|
7114 |
else:
|
7115 |
ptcs = [e[ptcs_index] for e in escore_notes if e[chans_index] != 9]
|
7116 |
+
vels = [e[vels_index] for e in escore_notes if e[chans_index] != 9]
|
7117 |
+
|
7118 |
+
if len(ptcs) == 0:
|
7119 |
+
ptcs = [0]
|
7120 |
+
|
7121 |
+
if len(vels) == 0:
|
7122 |
+
vels = [0]
|
7123 |
|
7124 |
return [sum(times) / len(times), sum(durs) / len(durs), sum(ptcs) / len(ptcs), sum(vels) / len(vels)]
|
7125 |
|
|
|
7899 |
chord = []
|
7900 |
|
7901 |
for cc in c:
|
|
|
7902 |
|
7903 |
+
if cc[channels_index] != 9:
|
7904 |
+
if cc[pitches_index] not in seen:
|
7905 |
+
|
7906 |
cc[channels_index] = 0
|
7907 |
cc[patches_index] = 0
|
7908 |
+
|
7909 |
chord.append(cc)
|
7910 |
seen.append(cc[pitches_index])
|
7911 |
+
|
7912 |
+
else:
|
7913 |
+
if keep_drums:
|
7914 |
+
if cc[pitches_index]+128 not in seen:
|
7915 |
chord.append(cc)
|
7916 |
+
seen.append(cc[pitches_index]+128)
|
7917 |
|
7918 |
sp_escore_notes.append(chord)
|
7919 |
|
|
|
9272 |
|
9273 |
return chunk_escore
|
9274 |
|
9275 |
+
###################################################################################
|
9276 |
+
|
9277 |
+
def advanced_add_drums_to_escore_notes(escore_notes,
|
9278 |
+
main_beat_min_dtime=5,
|
9279 |
+
main_beat_dtime_thres=1,
|
9280 |
+
drums_durations_value=2,
|
9281 |
+
drums_pitches_velocities=[(36, 100),
|
9282 |
+
(38, 100),
|
9283 |
+
(41, 125)],
|
9284 |
+
recalculate_score_timings=True,
|
9285 |
+
intro_drums_count=4,
|
9286 |
+
intro_drums_time_k=4,
|
9287 |
+
intro_drums_pitch_velocity=[37, 110]
|
9288 |
+
):
|
9289 |
+
|
9290 |
+
#===========================================================
|
9291 |
+
|
9292 |
+
new_dscore = delta_score_notes(escore_notes)
|
9293 |
+
|
9294 |
+
times = [d[1] for d in new_dscore if d[1] != 0]
|
9295 |
+
|
9296 |
+
time = [c[0] for c in Counter(times).most_common() if c[0] >= main_beat_min_dtime][0]
|
9297 |
+
|
9298 |
+
#===========================================================
|
9299 |
+
|
9300 |
+
if intro_drums_count > 0:
|
9301 |
+
|
9302 |
+
drums_score = []
|
9303 |
+
|
9304 |
+
for i in range(intro_drums_count):
|
9305 |
+
|
9306 |
+
if i == 0:
|
9307 |
+
dtime = 0
|
9308 |
+
|
9309 |
+
else:
|
9310 |
+
dtime = time
|
9311 |
+
|
9312 |
+
drums_score.append(['note',
|
9313 |
+
dtime * intro_drums_time_k,
|
9314 |
+
drums_durations_value,
|
9315 |
+
9,
|
9316 |
+
intro_drums_pitch_velocity[0],
|
9317 |
+
intro_drums_pitch_velocity[1],
|
9318 |
+
128]
|
9319 |
+
)
|
9320 |
+
|
9321 |
+
new_dscore[0][1] = time * intro_drums_time_k
|
9322 |
+
|
9323 |
+
new_dscore = drums_score + new_dscore
|
9324 |
+
|
9325 |
+
#===========================================================
|
9326 |
+
|
9327 |
+
for e in new_dscore:
|
9328 |
+
|
9329 |
+
if abs(e[1] - time) == main_beat_dtime_thres:
|
9330 |
+
e[1] = time
|
9331 |
+
|
9332 |
+
if recalculate_score_timings:
|
9333 |
+
|
9334 |
+
if e[1] % time != 0 and e[1] > time:
|
9335 |
+
if e[1] % time < time // 2:
|
9336 |
+
e[1] -= e[1] % time
|
9337 |
+
|
9338 |
+
else:
|
9339 |
+
e[1] += time - (e[1] % time)
|
9340 |
+
|
9341 |
+
#===========================================================
|
9342 |
+
|
9343 |
+
drums_score = []
|
9344 |
+
|
9345 |
+
dtime = 0
|
9346 |
+
|
9347 |
+
idx = 0
|
9348 |
+
|
9349 |
+
for i, e in enumerate(new_dscore):
|
9350 |
+
|
9351 |
+
drums_score.append(e)
|
9352 |
+
|
9353 |
+
dtime += e[1]
|
9354 |
+
|
9355 |
+
if e[1] != 0:
|
9356 |
+
idx += 1
|
9357 |
+
|
9358 |
+
if i >= intro_drums_count:
|
9359 |
+
|
9360 |
+
if (e[1] % time == 0 and e[1] != 0) or i == 0:
|
9361 |
+
|
9362 |
+
if idx % 2 == 0 and e[1] != 0:
|
9363 |
+
drums_score.append(['note',
|
9364 |
+
0,
|
9365 |
+
drums_durations_value,
|
9366 |
+
9,
|
9367 |
+
drums_pitches_velocities[0][0],
|
9368 |
+
drums_pitches_velocities[0][1],
|
9369 |
+
128]
|
9370 |
+
)
|
9371 |
+
|
9372 |
+
if idx % 2 != 0 and e[1] != 0:
|
9373 |
+
drums_score.append(['note',
|
9374 |
+
0,
|
9375 |
+
drums_durations_value,
|
9376 |
+
9,
|
9377 |
+
drums_pitches_velocities[1][0],
|
9378 |
+
drums_pitches_velocities[1][1],
|
9379 |
+
128]
|
9380 |
+
)
|
9381 |
+
|
9382 |
+
if idx % 4 == 0 and e[1] != 0:
|
9383 |
+
drums_score.append(['note',
|
9384 |
+
0,
|
9385 |
+
drums_durations_value,
|
9386 |
+
9,
|
9387 |
+
drums_pitches_velocities[2][0],
|
9388 |
+
drums_pitches_velocities[2][1],
|
9389 |
+
128]
|
9390 |
+
)
|
9391 |
+
|
9392 |
+
#===========================================================
|
9393 |
+
|
9394 |
+
return delta_score_to_abs_score(drums_score)
|
9395 |
+
|
9396 |
+
###################################################################################
|
9397 |
+
|
9398 |
+
MIDI_TEXT_EVENTS = ['text_event',
|
9399 |
+
'copyright_text_event',
|
9400 |
+
'track_name',
|
9401 |
+
'instrument_name',
|
9402 |
+
'lyric',
|
9403 |
+
'marker',
|
9404 |
+
'cue_point',
|
9405 |
+
'text_event_08',
|
9406 |
+
'text_event_09',
|
9407 |
+
'text_event_0a',
|
9408 |
+
'text_event_0b',
|
9409 |
+
'text_event_0c',
|
9410 |
+
'text_event_0d',
|
9411 |
+
'text_event_0e',
|
9412 |
+
'text_event_0f'
|
9413 |
+
]
|
9414 |
+
|
9415 |
+
###################################################################################
|
9416 |
+
|
9417 |
+
import hashlib
|
9418 |
+
import re
|
9419 |
+
|
9420 |
+
###################################################################################
|
9421 |
+
|
9422 |
+
def get_md5_hash(data):
|
9423 |
+
return hashlib.md5(data).hexdigest()
|
9424 |
+
|
9425 |
+
###################################################################################
|
9426 |
+
|
9427 |
+
def is_valid_md5_hash(string):
|
9428 |
+
return bool(re.match(r'^[a-fA-F0-9]{32}$', string))
|
9429 |
+
|
9430 |
+
###################################################################################
|
9431 |
+
|
9432 |
+
def clean_string(original_string,
|
9433 |
+
regex=r'[^a-zA-Z0-9 ]',
|
9434 |
+
remove_duplicate_spaces=True,
|
9435 |
+
title=False
|
9436 |
+
):
|
9437 |
+
|
9438 |
+
cstr1 = re.sub(regex, '', original_string)
|
9439 |
+
|
9440 |
+
if title:
|
9441 |
+
cstr1 = cstr1.title()
|
9442 |
+
|
9443 |
+
if remove_duplicate_spaces:
|
9444 |
+
return re.sub(r'\s+', ' ', cstr1).strip()
|
9445 |
+
|
9446 |
+
else:
|
9447 |
+
return cstr1
|
9448 |
+
|
9449 |
+
###################################################################################
|
9450 |
+
|
9451 |
+
def encode_to_ord(text, chars_range=[], sub_char='', chars_shift=0):
|
9452 |
+
|
9453 |
+
if not chars_range:
|
9454 |
+
chars_range = [32] + list(range(65, 91)) + list(range(97, 123))
|
9455 |
+
|
9456 |
+
if sub_char:
|
9457 |
+
chars_range.append(ord(sub_char))
|
9458 |
+
|
9459 |
+
chars_range = sorted(set(chars_range))
|
9460 |
+
|
9461 |
+
encoded = []
|
9462 |
+
|
9463 |
+
for char in text:
|
9464 |
+
if ord(char) in chars_range:
|
9465 |
+
encoded.append(chars_range.index(ord(char)) + chars_shift)
|
9466 |
+
|
9467 |
+
else:
|
9468 |
+
if sub_char:
|
9469 |
+
encoded.append(chars_range.index(ord(sub_char)) + chars_shift)
|
9470 |
+
|
9471 |
+
|
9472 |
+
return [encoded, chars_range]
|
9473 |
+
|
9474 |
+
###################################################################################
|
9475 |
+
|
9476 |
+
def decode_from_ord(ord_list, chars_range=[], sub_char='', chars_shift=0):
|
9477 |
+
|
9478 |
+
if not chars_range:
|
9479 |
+
chars_range = [32] + list(range(65, 91)) + list(range(97, 123))
|
9480 |
+
|
9481 |
+
if sub_char:
|
9482 |
+
chars_range.append(ord(sub_char))
|
9483 |
+
|
9484 |
+
chars_range = sorted(set(chars_range))
|
9485 |
+
|
9486 |
+
return ''.join(chr(chars_range[num-chars_shift]) if 0 <= num-chars_shift < len(chars_range) else sub_char for num in ord_list)
|
9487 |
+
|
9488 |
+
###################################################################################
|
9489 |
+
|
9490 |
+
def lists_similarity(list1, list2, by_elements=True, by_sum=True):
|
9491 |
+
|
9492 |
+
if len(list1) != len(list2):
|
9493 |
+
return -1
|
9494 |
+
|
9495 |
+
element_ratios = []
|
9496 |
+
total_counts1 = sum(list1)
|
9497 |
+
total_counts2 = sum(list2)
|
9498 |
+
|
9499 |
+
for a, b in zip(list1, list2):
|
9500 |
+
if a == 0 and b == 0:
|
9501 |
+
element_ratios.append(1)
|
9502 |
+
elif a == 0 or b == 0:
|
9503 |
+
element_ratios.append(0)
|
9504 |
+
else:
|
9505 |
+
element_ratios.append(min(a, b) / max(a, b))
|
9506 |
+
|
9507 |
+
average_element_ratio = sum(element_ratios) / len(element_ratios)
|
9508 |
+
|
9509 |
+
total_counts_ratio = min(total_counts1, total_counts2) / max(total_counts1, total_counts2)
|
9510 |
+
|
9511 |
+
if by_elements and by_sum:
|
9512 |
+
return (average_element_ratio + total_counts_ratio) / 2
|
9513 |
+
|
9514 |
+
elif by_elements and not by_sum:
|
9515 |
+
return average_element_ratio
|
9516 |
+
|
9517 |
+
elif not by_elements and by_sum:
|
9518 |
+
return total_counts_ratio
|
9519 |
+
|
9520 |
+
else:
|
9521 |
+
return -1
|
9522 |
+
|
9523 |
+
###################################################################################
|
9524 |
+
|
9525 |
+
def find_indexes(lst, value, mode='equal', dual_mode=True):
|
9526 |
+
|
9527 |
+
indexes = []
|
9528 |
+
|
9529 |
+
if mode == 'equal' or dual_mode:
|
9530 |
+
indexes.extend([index for index, elem in enumerate(lst) if elem == value])
|
9531 |
+
|
9532 |
+
if mode == 'smaller':
|
9533 |
+
indexes.extend([index for index, elem in enumerate(lst) if elem < value])
|
9534 |
+
|
9535 |
+
if mode == 'larger':
|
9536 |
+
indexes.extend([index for index, elem in enumerate(lst) if elem > value])
|
9537 |
+
|
9538 |
+
return sorted(set(indexes))
|
9539 |
+
|
9540 |
+
###################################################################################
|
9541 |
+
|
9542 |
+
NUMERALS = ["one", "two", "three", "four",
|
9543 |
+
"five", "six", "seven", "eight",
|
9544 |
+
"nine", "ten", "eleven", "twelve",
|
9545 |
+
"thirteen", "fourteen", "fifteen", "sixteen"
|
9546 |
+
]
|
9547 |
+
|
9548 |
+
SEMITONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
|
9549 |
+
|
9550 |
+
MOOD_SCALES = ['Major', 'Minor', 'Major Minor']
|
9551 |
+
|
9552 |
+
###################################################################################
|
9553 |
+
|
9554 |
+
def alpha_str(string):
|
9555 |
+
return re.sub(r'[^a-zA-Z ()]', '', string).strip()
|
9556 |
+
|
9557 |
+
###################################################################################
|
9558 |
+
|
9559 |
+
def escore_notes_to_text_description(escore_notes, song_name='', artist_name=''):
|
9560 |
+
|
9561 |
+
#==============================================================================
|
9562 |
+
|
9563 |
+
song_time_min = (escore_notes[-1][1] * 16) / 1000 / 60
|
9564 |
+
|
9565 |
+
if song_time_min < 1.5:
|
9566 |
+
song_length = 'short'
|
9567 |
+
|
9568 |
+
elif 1.5 <= song_time_min < 2.5:
|
9569 |
+
song_length = 'average'
|
9570 |
+
|
9571 |
+
elif song_time_min >= 2.5:
|
9572 |
+
song_length = 'long'
|
9573 |
+
|
9574 |
+
#==============================================================================
|
9575 |
+
|
9576 |
+
escore_times = [e[1] for e in escore_notes if e[3] != 9]
|
9577 |
+
|
9578 |
+
comp_type = ''
|
9579 |
+
|
9580 |
+
if len(escore_times) > 0:
|
9581 |
+
if len(escore_times) == len(set(escore_times)):
|
9582 |
+
comp_type = 'monophonic melody'
|
9583 |
+
|
9584 |
+
elif len(escore_times) >= len(set(escore_times)) and 1 in Counter(escore_times).values():
|
9585 |
+
comp_type = 'melody and accompaniment'
|
9586 |
+
|
9587 |
+
elif len(escore_times) >= len(set(escore_times)) and 1 not in Counter(escore_times).values():
|
9588 |
+
comp_type = 'accompaniment'
|
9589 |
+
|
9590 |
+
else:
|
9591 |
+
comp_type = 'drums only'
|
9592 |
+
|
9593 |
+
#==============================================================================
|
9594 |
+
|
9595 |
+
patches = sorted(set([e[6] for e in escore_notes]))
|
9596 |
+
|
9597 |
+
instruments = [alpha_str(Number2patch[p]) for p in patches if p < 128]
|
9598 |
+
|
9599 |
+
if 128 in patches:
|
9600 |
+
drums_present = True
|
9601 |
+
|
9602 |
+
else:
|
9603 |
+
drums_present = False
|
9604 |
+
|
9605 |
+
drums_pitches = [e[4] for e in escore_notes if e[3] == 9]
|
9606 |
+
|
9607 |
+
most_common_drums = [alpha_str(Notenum2percussion[p[0]]) for p in Counter(drums_pitches).most_common(3) if p[0] in Notenum2percussion]
|
9608 |
+
|
9609 |
+
#==============================================================================
|
9610 |
+
|
9611 |
+
pitches = [e[4] for e in escore_notes if e[3] != 9]
|
9612 |
+
|
9613 |
+
key = ''
|
9614 |
+
|
9615 |
+
if pitches:
|
9616 |
+
key = SEMITONES[statistics.mode(pitches) % 12]
|
9617 |
+
|
9618 |
+
#==============================================================================
|
9619 |
+
|
9620 |
+
mood = ''
|
9621 |
+
|
9622 |
+
if pitches:
|
9623 |
+
|
9624 |
+
cscore = chordify_score([1000, escore_notes])
|
9625 |
+
|
9626 |
+
tones_chords = Counter()
|
9627 |
+
|
9628 |
+
for c in cscore:
|
9629 |
+
if len([e for e in c if e[3] != 9]) > 0:
|
9630 |
+
tones_chords[tuple(sorted(set([e[4] % 12 for e in c if e[3] != 9])))] += 1
|
9631 |
+
|
9632 |
+
most_common_tones_chords = [check_and_fix_tones_chord(list(c[0])) for c in tones_chords.most_common(10)]
|
9633 |
+
|
9634 |
+
mood_scale = statistics.mode(tones_chords_to_types(most_common_tones_chords, return_chord_type_index=True)) % 3
|
9635 |
+
|
9636 |
+
mood = MOOD_SCALES[mood_scale]
|
9637 |
+
|
9638 |
+
#==============================================================================
|
9639 |
+
|
9640 |
+
if pitches:
|
9641 |
+
|
9642 |
+
escore_averages = escore_notes_averages(escore_notes, return_ptcs_and_vels=True)
|
9643 |
+
|
9644 |
+
if escore_averages[0] < 8:
|
9645 |
+
rythm = 'fast'
|
9646 |
+
|
9647 |
+
elif 8 <= escore_averages[0] <= 12:
|
9648 |
+
rythm = 'average'
|
9649 |
+
|
9650 |
+
elif escore_averages[0] > 12:
|
9651 |
+
rythm = 'slow'
|
9652 |
+
|
9653 |
+
if escore_averages[1] < 16:
|
9654 |
+
tempo = 'fast'
|
9655 |
+
|
9656 |
+
elif 16 <= escore_averages[1] <= 24:
|
9657 |
+
tempo = 'average'
|
9658 |
+
|
9659 |
+
elif escore_averages[1] > 24:
|
9660 |
+
tempo = 'slow'
|
9661 |
+
|
9662 |
+
if escore_averages[2] < 50:
|
9663 |
+
tone = 'bass'
|
9664 |
+
|
9665 |
+
elif 50 <= escore_averages[2] <= 70:
|
9666 |
+
tone = 'midrange'
|
9667 |
+
|
9668 |
+
elif escore_averages[2] > 70:
|
9669 |
+
tone = 'treble'
|
9670 |
+
|
9671 |
+
if escore_averages[3] < 80:
|
9672 |
+
dynamics = 'quiet'
|
9673 |
+
|
9674 |
+
elif 80 <= escore_averages[3] <= 100:
|
9675 |
+
dynamics = 'average'
|
9676 |
+
|
9677 |
+
elif escore_averages[3] > 100:
|
9678 |
+
dynamics = 'loud'
|
9679 |
+
|
9680 |
+
#==============================================================================
|
9681 |
+
|
9682 |
+
description = ''
|
9683 |
+
|
9684 |
+
if song_name != '':
|
9685 |
+
description = 'Song "' + song_name + '"'
|
9686 |
+
|
9687 |
+
if artist_name != '':
|
9688 |
+
description += ' by ' + artist_name
|
9689 |
+
|
9690 |
+
if song_name != '' or artist_name != '':
|
9691 |
+
description += '.'
|
9692 |
+
description += '\n'
|
9693 |
+
|
9694 |
+
description += 'The song is '
|
9695 |
+
|
9696 |
+
if song_length != 'average':
|
9697 |
+
description += 'a ' + song_length
|
9698 |
+
|
9699 |
+
else:
|
9700 |
+
description += 'an ' + song_length
|
9701 |
+
|
9702 |
+
description += ' duration '
|
9703 |
+
|
9704 |
+
description += comp_type + ' composition'
|
9705 |
+
|
9706 |
+
if comp_type != 'drum track':
|
9707 |
+
|
9708 |
+
if drums_present:
|
9709 |
+
description += ' with drums'
|
9710 |
+
|
9711 |
+
else:
|
9712 |
+
description += ' without drums'
|
9713 |
+
|
9714 |
+
if key and mood:
|
9715 |
+
description += ' in ' + key + ' ' + mood
|
9716 |
+
|
9717 |
+
description += '.'
|
9718 |
+
|
9719 |
+
description += '\n'
|
9720 |
+
|
9721 |
+
if pitches:
|
9722 |
+
|
9723 |
+
description += 'It has '
|
9724 |
+
|
9725 |
+
description += rythm + ' rythm, '
|
9726 |
+
description += tempo + ' tempo, '
|
9727 |
+
description += tone + ' tone and '
|
9728 |
+
description += dynamics + ' dynamics.'
|
9729 |
+
|
9730 |
+
description += '\n'
|
9731 |
+
|
9732 |
+
description += 'The song '
|
9733 |
+
|
9734 |
+
if len(instruments) == 1:
|
9735 |
+
description += 'is played on a solo ' + instruments[0] + '.'
|
9736 |
+
|
9737 |
+
else:
|
9738 |
+
description += 'features ' + NUMERALS[max(0, min(15, len(instruments)-1))] + ' instruments: '
|
9739 |
+
description += ', '.join(instruments[:-1]) + ' and ' + instruments[-1] + '.'
|
9740 |
+
|
9741 |
+
description += '\n'
|
9742 |
+
|
9743 |
+
if drums_present and most_common_drums:
|
9744 |
+
description += 'The drum track has predominant '
|
9745 |
+
description += ', '.join(most_common_drums[:-1]) + ' and ' + most_common_drums[-1] + '.'
|
9746 |
+
|
9747 |
+
#==============================================================================
|
9748 |
+
|
9749 |
+
return description
|
9750 |
+
|
9751 |
+
###################################################################################
|
9752 |
+
|
9753 |
+
#==================================================================================
|
9754 |
+
#
|
9755 |
+
# Below constants code is a courtesy of MidiTok
|
9756 |
+
#
|
9757 |
+
# Retrieved on 12/29/2024
|
9758 |
+
#
|
9759 |
+
# https://github.com/Natooz/MidiTok/blob/main/src/miditok/constants.py
|
9760 |
+
#
|
9761 |
+
#==================================================================================
|
9762 |
+
|
9763 |
+
MIDI_FILES_EXTENSIONS = [".mid", ".midi", ".kar", ".MID", ".MIDI", ".KAR"]
|
9764 |
+
|
9765 |
+
# The recommended pitches for piano in the GM2 specs are from 21 to 108
|
9766 |
+
PIANO_PITCH_RANGE = range(21, 109)
|
9767 |
+
|
9768 |
+
# Chord params
|
9769 |
+
# "chord_unknown" specifies the range of number of notes that can form "unknown" chords
|
9770 |
+
# (that do not fit in "chord_maps") to add in tokens.
|
9771 |
+
# Known chord maps, with 0 as root note
|
9772 |
+
BASIC_CHORDS_MAP = {
|
9773 |
+
"min": (0, 3, 7),
|
9774 |
+
"maj": (0, 4, 7),
|
9775 |
+
"dim": (0, 3, 6),
|
9776 |
+
"aug": (0, 4, 8),
|
9777 |
+
"sus2": (0, 2, 7),
|
9778 |
+
"sus4": (0, 5, 7),
|
9779 |
+
"7dom": (0, 4, 7, 10),
|
9780 |
+
"7min": (0, 3, 7, 10),
|
9781 |
+
"7maj": (0, 4, 7, 11),
|
9782 |
+
"7halfdim": (0, 3, 6, 10),
|
9783 |
+
"7dim": (0, 3, 6, 9),
|
9784 |
+
"7aug": (0, 4, 8, 11),
|
9785 |
+
"9maj": (0, 4, 7, 10, 14),
|
9786 |
+
"9min": (0, 4, 7, 10, 13),
|
9787 |
+
}
|
9788 |
+
|
9789 |
+
# Drums
|
9790 |
+
# Recommended range from the GM2 specs
|
9791 |
+
DRUMS_PITCH_RANGE = range(27, 90)
|
9792 |
+
|
9793 |
+
# Used with chords
|
9794 |
+
PITCH_CLASSES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
|
9795 |
+
|
9796 |
+
# http://newt.phys.unsw.edu.au/jw/notes.html
|
9797 |
+
# https://www.midi.org/specifications
|
9798 |
+
|
9799 |
+
# index i = program i+1 in the GM2 specs (7. Appendix A)
|
9800 |
+
# index i = program i as retrieved by packages
|
9801 |
+
MIDI_INSTRUMENTS = [
|
9802 |
+
# Piano
|
9803 |
+
{"name": "Acoustic Grand Piano", "pitch_range": range(21, 109)},
|
9804 |
+
{"name": "Bright Acoustic Piano", "pitch_range": range(21, 109)},
|
9805 |
+
{"name": "Electric Grand Piano", "pitch_range": range(21, 109)},
|
9806 |
+
{"name": "Honky-tonk Piano", "pitch_range": range(21, 109)},
|
9807 |
+
{"name": "Electric Piano 1", "pitch_range": range(28, 104)},
|
9808 |
+
{"name": "Electric Piano 2", "pitch_range": range(28, 104)},
|
9809 |
+
{"name": "Harpsichord", "pitch_range": range(41, 90)},
|
9810 |
+
{"name": "Clavi", "pitch_range": range(36, 97)},
|
9811 |
+
# Chromatic Percussion
|
9812 |
+
{"name": "Celesta", "pitch_range": range(60, 109)},
|
9813 |
+
{"name": "Glockenspiel", "pitch_range": range(72, 109)},
|
9814 |
+
{"name": "Music Box", "pitch_range": range(60, 85)},
|
9815 |
+
{"name": "Vibraphone", "pitch_range": range(53, 90)},
|
9816 |
+
{"name": "Marimba", "pitch_range": range(48, 85)},
|
9817 |
+
{"name": "Xylophone", "pitch_range": range(65, 97)},
|
9818 |
+
{"name": "Tubular Bells", "pitch_range": range(60, 78)},
|
9819 |
+
{"name": "Dulcimer", "pitch_range": range(60, 85)},
|
9820 |
+
# Organs
|
9821 |
+
{"name": "Drawbar Organ", "pitch_range": range(36, 97)},
|
9822 |
+
{"name": "Percussive Organ", "pitch_range": range(36, 97)},
|
9823 |
+
{"name": "Rock Organ", "pitch_range": range(36, 97)},
|
9824 |
+
{"name": "Church Organ", "pitch_range": range(21, 109)},
|
9825 |
+
{"name": "Reed Organ", "pitch_range": range(36, 97)},
|
9826 |
+
{"name": "Accordion", "pitch_range": range(53, 90)},
|
9827 |
+
{"name": "Harmonica", "pitch_range": range(60, 85)},
|
9828 |
+
{"name": "Tango Accordion", "pitch_range": range(53, 90)},
|
9829 |
+
# Guitars
|
9830 |
+
{"name": "Acoustic Guitar (nylon)", "pitch_range": range(40, 85)},
|
9831 |
+
{"name": "Acoustic Guitar (steel)", "pitch_range": range(40, 85)},
|
9832 |
+
{"name": "Electric Guitar (jazz)", "pitch_range": range(40, 87)},
|
9833 |
+
{"name": "Electric Guitar (clean)", "pitch_range": range(40, 87)},
|
9834 |
+
{"name": "Electric Guitar (muted)", "pitch_range": range(40, 87)},
|
9835 |
+
{"name": "Overdriven Guitar", "pitch_range": range(40, 87)},
|
9836 |
+
{"name": "Distortion Guitar", "pitch_range": range(40, 87)},
|
9837 |
+
{"name": "Guitar Harmonics", "pitch_range": range(40, 87)},
|
9838 |
+
# Bass
|
9839 |
+
{"name": "Acoustic Bass", "pitch_range": range(28, 56)},
|
9840 |
+
{"name": "Electric Bass (finger)", "pitch_range": range(28, 56)},
|
9841 |
+
{"name": "Electric Bass (pick)", "pitch_range": range(28, 56)},
|
9842 |
+
{"name": "Fretless Bass", "pitch_range": range(28, 56)},
|
9843 |
+
{"name": "Slap Bass 1", "pitch_range": range(28, 56)},
|
9844 |
+
{"name": "Slap Bass 2", "pitch_range": range(28, 56)},
|
9845 |
+
{"name": "Synth Bass 1", "pitch_range": range(28, 56)},
|
9846 |
+
{"name": "Synth Bass 2", "pitch_range": range(28, 56)},
|
9847 |
+
# Strings & Orchestral instruments
|
9848 |
+
{"name": "Violin", "pitch_range": range(55, 94)},
|
9849 |
+
{"name": "Viola", "pitch_range": range(48, 85)},
|
9850 |
+
{"name": "Cello", "pitch_range": range(36, 73)},
|
9851 |
+
{"name": "Contrabass", "pitch_range": range(28, 56)},
|
9852 |
+
{"name": "Tremolo Strings", "pitch_range": range(28, 94)},
|
9853 |
+
{"name": "Pizzicato Strings", "pitch_range": range(28, 94)},
|
9854 |
+
{"name": "Orchestral Harp", "pitch_range": range(23, 104)},
|
9855 |
+
{"name": "Timpani", "pitch_range": range(36, 58)},
|
9856 |
+
# Ensembles
|
9857 |
+
{"name": "String Ensembles 1", "pitch_range": range(28, 97)},
|
9858 |
+
{"name": "String Ensembles 2", "pitch_range": range(28, 97)},
|
9859 |
+
{"name": "SynthStrings 1", "pitch_range": range(36, 97)},
|
9860 |
+
{"name": "SynthStrings 2", "pitch_range": range(36, 97)},
|
9861 |
+
{"name": "Choir Aahs", "pitch_range": range(48, 80)},
|
9862 |
+
{"name": "Voice Oohs", "pitch_range": range(48, 80)},
|
9863 |
+
{"name": "Synth Voice", "pitch_range": range(48, 85)},
|
9864 |
+
{"name": "Orchestra Hit", "pitch_range": range(48, 73)},
|
9865 |
+
# Brass
|
9866 |
+
{"name": "Trumpet", "pitch_range": range(58, 95)},
|
9867 |
+
{"name": "Trombone", "pitch_range": range(34, 76)},
|
9868 |
+
{"name": "Tuba", "pitch_range": range(29, 56)},
|
9869 |
+
{"name": "Muted Trumpet", "pitch_range": range(58, 83)},
|
9870 |
+
{"name": "French Horn", "pitch_range": range(41, 78)},
|
9871 |
+
{"name": "Brass Section", "pitch_range": range(36, 97)},
|
9872 |
+
{"name": "Synth Brass 1", "pitch_range": range(36, 97)},
|
9873 |
+
{"name": "Synth Brass 2", "pitch_range": range(36, 97)},
|
9874 |
+
# Reed
|
9875 |
+
{"name": "Soprano Sax", "pitch_range": range(54, 88)},
|
9876 |
+
{"name": "Alto Sax", "pitch_range": range(49, 81)},
|
9877 |
+
{"name": "Tenor Sax", "pitch_range": range(42, 76)},
|
9878 |
+
{"name": "Baritone Sax", "pitch_range": range(37, 69)},
|
9879 |
+
{"name": "Oboe", "pitch_range": range(58, 92)},
|
9880 |
+
{"name": "English Horn", "pitch_range": range(52, 82)},
|
9881 |
+
{"name": "Bassoon", "pitch_range": range(34, 73)},
|
9882 |
+
{"name": "Clarinet", "pitch_range": range(50, 92)},
|
9883 |
+
# Pipe
|
9884 |
+
{"name": "Piccolo", "pitch_range": range(74, 109)},
|
9885 |
+
{"name": "Flute", "pitch_range": range(60, 97)},
|
9886 |
+
{"name": "Recorder", "pitch_range": range(60, 97)},
|
9887 |
+
{"name": "Pan Flute", "pitch_range": range(60, 97)},
|
9888 |
+
{"name": "Blown Bottle", "pitch_range": range(60, 97)},
|
9889 |
+
{"name": "Shakuhachi", "pitch_range": range(55, 85)},
|
9890 |
+
{"name": "Whistle", "pitch_range": range(60, 97)},
|
9891 |
+
{"name": "Ocarina", "pitch_range": range(60, 85)},
|
9892 |
+
# Synth Lead
|
9893 |
+
{"name": "Lead 1 (square)", "pitch_range": range(21, 109)},
|
9894 |
+
{"name": "Lead 2 (sawtooth)", "pitch_range": range(21, 109)},
|
9895 |
+
{"name": "Lead 3 (calliope)", "pitch_range": range(36, 97)},
|
9896 |
+
{"name": "Lead 4 (chiff)", "pitch_range": range(36, 97)},
|
9897 |
+
{"name": "Lead 5 (charang)", "pitch_range": range(36, 97)},
|
9898 |
+
{"name": "Lead 6 (voice)", "pitch_range": range(36, 97)},
|
9899 |
+
{"name": "Lead 7 (fifths)", "pitch_range": range(36, 97)},
|
9900 |
+
{"name": "Lead 8 (bass + lead)", "pitch_range": range(21, 109)},
|
9901 |
+
# Synth Pad
|
9902 |
+
{"name": "Pad 1 (new age)", "pitch_range": range(36, 97)},
|
9903 |
+
{"name": "Pad 2 (warm)", "pitch_range": range(36, 97)},
|
9904 |
+
{"name": "Pad 3 (polysynth)", "pitch_range": range(36, 97)},
|
9905 |
+
{"name": "Pad 4 (choir)", "pitch_range": range(36, 97)},
|
9906 |
+
{"name": "Pad 5 (bowed)", "pitch_range": range(36, 97)},
|
9907 |
+
{"name": "Pad 6 (metallic)", "pitch_range": range(36, 97)},
|
9908 |
+
{"name": "Pad 7 (halo)", "pitch_range": range(36, 97)},
|
9909 |
+
{"name": "Pad 8 (sweep)", "pitch_range": range(36, 97)},
|
9910 |
+
# Synth SFX
|
9911 |
+
{"name": "FX 1 (rain)", "pitch_range": range(36, 97)},
|
9912 |
+
{"name": "FX 2 (soundtrack)", "pitch_range": range(36, 97)},
|
9913 |
+
{"name": "FX 3 (crystal)", "pitch_range": range(36, 97)},
|
9914 |
+
{"name": "FX 4 (atmosphere)", "pitch_range": range(36, 97)},
|
9915 |
+
{"name": "FX 5 (brightness)", "pitch_range": range(36, 97)},
|
9916 |
+
{"name": "FX 6 (goblins)", "pitch_range": range(36, 97)},
|
9917 |
+
{"name": "FX 7 (echoes)", "pitch_range": range(36, 97)},
|
9918 |
+
{"name": "FX 8 (sci-fi)", "pitch_range": range(36, 97)},
|
9919 |
+
# Ethnic Misc.
|
9920 |
+
{"name": "Sitar", "pitch_range": range(48, 78)},
|
9921 |
+
{"name": "Banjo", "pitch_range": range(48, 85)},
|
9922 |
+
{"name": "Shamisen", "pitch_range": range(50, 80)},
|
9923 |
+
{"name": "Koto", "pitch_range": range(55, 85)},
|
9924 |
+
{"name": "Kalimba", "pitch_range": range(48, 80)},
|
9925 |
+
{"name": "Bag pipe", "pitch_range": range(36, 78)},
|
9926 |
+
{"name": "Fiddle", "pitch_range": range(55, 97)},
|
9927 |
+
{"name": "Shanai", "pitch_range": range(48, 73)},
|
9928 |
+
# Percussive
|
9929 |
+
{"name": "Tinkle Bell", "pitch_range": range(72, 85)},
|
9930 |
+
{"name": "Agogo", "pitch_range": range(60, 73)},
|
9931 |
+
{"name": "Steel Drums", "pitch_range": range(52, 77)},
|
9932 |
+
{"name": "Woodblock", "pitch_range": range(128)},
|
9933 |
+
{"name": "Taiko Drum", "pitch_range": range(128)},
|
9934 |
+
{"name": "Melodic Tom", "pitch_range": range(128)},
|
9935 |
+
{"name": "Synth Drum", "pitch_range": range(128)},
|
9936 |
+
{"name": "Reverse Cymbal", "pitch_range": range(128)},
|
9937 |
+
# SFX
|
9938 |
+
{"name": "Guitar Fret Noise, Guitar Cutting Noise", "pitch_range": range(128)},
|
9939 |
+
{"name": "Breath Noise, Flute Key Click", "pitch_range": range(128)},
|
9940 |
+
{
|
9941 |
+
"name": "Seashore, Rain, Thunder, Wind, Stream, Bubbles",
|
9942 |
+
"pitch_range": range(128),
|
9943 |
+
},
|
9944 |
+
{"name": "Bird Tweet, Dog, Horse Gallop", "pitch_range": range(128)},
|
9945 |
+
{
|
9946 |
+
"name": "Telephone Ring, Door Creaking, Door, Scratch, Wind Chime",
|
9947 |
+
"pitch_range": range(128),
|
9948 |
+
},
|
9949 |
+
{"name": "Helicopter, Car Sounds", "pitch_range": range(128)},
|
9950 |
+
{
|
9951 |
+
"name": "Applause, Laughing, Screaming, Punch, Heart Beat, Footstep",
|
9952 |
+
"pitch_range": range(128),
|
9953 |
+
},
|
9954 |
+
{"name": "Gunshot, Machine Gun, Lasergun, Explosion", "pitch_range": range(128)},
|
9955 |
+
]
|
9956 |
+
|
9957 |
+
INSTRUMENTS_CLASSES = [
|
9958 |
+
{"name": "Piano", "program_range": range(8)}, # 0
|
9959 |
+
{"name": "Chromatic Percussion", "program_range": range(8, 16)},
|
9960 |
+
{"name": "Organ", "program_range": range(16, 24)},
|
9961 |
+
{"name": "Guitar", "program_range": range(24, 32)},
|
9962 |
+
{"name": "Bass", "program_range": range(32, 40)},
|
9963 |
+
{"name": "Strings", "program_range": range(40, 48)}, # 5
|
9964 |
+
{"name": "Ensemble", "program_range": range(48, 56)},
|
9965 |
+
{"name": "Brass", "program_range": range(56, 64)},
|
9966 |
+
{"name": "Reed", "program_range": range(64, 72)},
|
9967 |
+
{"name": "Pipe", "program_range": range(72, 80)},
|
9968 |
+
{"name": "Synth Lead", "program_range": range(80, 88)}, # 10
|
9969 |
+
{"name": "Synth Pad", "program_range": range(88, 96)},
|
9970 |
+
{"name": "Synth Effects", "program_range": range(96, 104)},
|
9971 |
+
{"name": "Ethnic", "program_range": range(104, 112)},
|
9972 |
+
{"name": "Percussive", "program_range": range(112, 120)},
|
9973 |
+
{"name": "Sound Effects", "program_range": range(120, 128)}, # 15
|
9974 |
+
{"name": "Drums", "program_range": range(-1, 0)},
|
9975 |
+
]
|
9976 |
+
|
9977 |
+
# To easily get the class index of any instrument program
|
9978 |
+
CLASS_OF_INST = [
|
9979 |
+
i
|
9980 |
+
for i, inst_class in enumerate(INSTRUMENTS_CLASSES)
|
9981 |
+
for _ in inst_class["program_range"]
|
9982 |
+
]
|
9983 |
+
|
9984 |
+
# index i = program i+1 in the GM2 specs (8. Appendix B)
|
9985 |
+
# index i = program i retrieved by packages
|
9986 |
+
DRUMS_SETS = {
|
9987 |
+
0: "Standard",
|
9988 |
+
8: "Room",
|
9989 |
+
16: "Power",
|
9990 |
+
24: "Electronic",
|
9991 |
+
25: "Analog",
|
9992 |
+
32: "Jazz",
|
9993 |
+
40: "Brush",
|
9994 |
+
48: "Orchestra",
|
9995 |
+
56: "SFX",
|
9996 |
+
}
|
9997 |
+
|
9998 |
+
# Control changes list (without specifications):
|
9999 |
+
# https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
|
10000 |
+
# Undefined and general control changes are not considered here
|
10001 |
+
# All these attributes can take values from 0 to 127, with some of them being on/off
|
10002 |
+
CONTROL_CHANGES = {
|
10003 |
+
# MSB
|
10004 |
+
0: "Bank Select",
|
10005 |
+
1: "Modulation Depth",
|
10006 |
+
2: "Breath Controller",
|
10007 |
+
4: "Foot Controller",
|
10008 |
+
5: "Portamento Time",
|
10009 |
+
6: "Data Entry",
|
10010 |
+
7: "Channel Volume",
|
10011 |
+
8: "Balance",
|
10012 |
+
10: "Pan",
|
10013 |
+
11: "Expression Controller",
|
10014 |
+
# LSB
|
10015 |
+
32: "Bank Select",
|
10016 |
+
33: "Modulation Depth",
|
10017 |
+
34: "Breath Controller",
|
10018 |
+
36: "Foot Controller",
|
10019 |
+
37: "Portamento Time",
|
10020 |
+
38: "Data Entry",
|
10021 |
+
39: "Channel Volume",
|
10022 |
+
40: "Balance",
|
10023 |
+
42: "Pan",
|
10024 |
+
43: "Expression Controller",
|
10025 |
+
# On / Off control changes, ≤63 off, ≥64 on
|
10026 |
+
64: "Damper Pedal",
|
10027 |
+
65: "Portamento",
|
10028 |
+
66: "Sostenuto",
|
10029 |
+
67: "Soft Pedal",
|
10030 |
+
68: "Legato Footswitch",
|
10031 |
+
69: "Hold 2",
|
10032 |
+
# Continuous controls
|
10033 |
+
70: "Sound Variation",
|
10034 |
+
71: "Timbre/Harmonic Intensity",
|
10035 |
+
72: "Release Time",
|
10036 |
+
73: "Attack Time",
|
10037 |
+
74: "Brightness",
|
10038 |
+
75: "Decay Time",
|
10039 |
+
76: "Vibrato Rate",
|
10040 |
+
77: "Vibrato Depth",
|
10041 |
+
78: "Vibrato Delay",
|
10042 |
+
84: "Portamento Control",
|
10043 |
+
88: "High Resolution Velocity Prefix",
|
10044 |
+
# Effects depths
|
10045 |
+
91: "Reverb Depth",
|
10046 |
+
92: "Tremolo Depth",
|
10047 |
+
93: "Chorus Depth",
|
10048 |
+
94: "Celeste Depth",
|
10049 |
+
95: "Phaser Depth",
|
10050 |
+
# Registered parameters numbers
|
10051 |
+
96: "Data Increment",
|
10052 |
+
97: "Data Decrement",
|
10053 |
+
# 98: 'Non-Registered Parameter Number (NRPN) - LSB',
|
10054 |
+
# 99: 'Non-Registered Parameter Number (NRPN) - MSB',
|
10055 |
+
100: "Registered Parameter Number (RPN) - LSB",
|
10056 |
+
101: "Registered Parameter Number (RPN) - MSB",
|
10057 |
+
# Channel mode controls
|
10058 |
+
120: "All Sound Off",
|
10059 |
+
121: "Reset All Controllers",
|
10060 |
+
122: "Local Control On/Off",
|
10061 |
+
123: "All Notes Off",
|
10062 |
+
124: "Omni Mode Off", # + all notes off
|
10063 |
+
125: "Omni Mode On", # + all notes off
|
10064 |
+
126: "Mono Mode On", # + poly off, + all notes off
|
10065 |
+
127: "Poly Mode On", # + mono off, +all notes off
|
10066 |
+
}
|
10067 |
+
|
10068 |
+
###################################################################################
|
10069 |
+
|
10070 |
+
def patches_onset_times(escore_notes, times_idx=1, patches_idx=6):
|
10071 |
+
|
10072 |
+
patches = [e[patches_idx] for e in escore_notes]
|
10073 |
+
|
10074 |
+
patches_oset = ordered_set(patches)
|
10075 |
+
|
10076 |
+
patches_onset_times = []
|
10077 |
+
|
10078 |
+
for p in patches_oset:
|
10079 |
+
for e in escore_notes:
|
10080 |
+
if e[patches_idx] == p:
|
10081 |
+
patches_onset_times.append([p, e[times_idx]])
|
10082 |
+
break
|
10083 |
+
|
10084 |
+
return patches_onset_times
|
10085 |
+
|
10086 |
+
###################################################################################
|
10087 |
+
|
10088 |
+
def count_escore_notes_patches(escore_notes, patches_idx=6):
|
10089 |
+
|
10090 |
+
patches = [e[patches_idx] for e in escore_notes]
|
10091 |
+
|
10092 |
+
return Counter(patches).most_common()
|
10093 |
+
|
10094 |
+
###################################################################################
|
10095 |
+
|
10096 |
+
def escore_notes_monoponic_melodies(escore_notes,
|
10097 |
+
bad_notes_ratio=0.0,
|
10098 |
+
times_idx=1,
|
10099 |
+
patches_idx=6
|
10100 |
+
):
|
10101 |
+
|
10102 |
+
patches = escore_notes_patches(escore_notes, patches_index=patches_idx)
|
10103 |
+
|
10104 |
+
monophonic_melodies = []
|
10105 |
+
|
10106 |
+
for p in patches:
|
10107 |
+
patch_score = [e for e in escore_notes if e[patches_idx] == p]
|
10108 |
+
|
10109 |
+
ps_times = [e[times_idx] for e in patch_score]
|
10110 |
+
|
10111 |
+
if len(ps_times) <= len(set(ps_times)) * (1+bad_notes_ratio):
|
10112 |
+
monophonic_melodies.append([p, len(patch_score)])
|
10113 |
+
|
10114 |
+
return monophonic_melodies
|
10115 |
+
|
10116 |
+
###################################################################################
|
10117 |
+
|
10118 |
+
from itertools import groupby
|
10119 |
+
from operator import itemgetter
|
10120 |
+
|
10121 |
+
def group_by_threshold(data, threshold, groupby_idx):
|
10122 |
+
|
10123 |
+
data.sort(key=itemgetter(groupby_idx))
|
10124 |
+
|
10125 |
+
grouped_data = []
|
10126 |
+
cluster = []
|
10127 |
+
|
10128 |
+
for i, item in enumerate(data):
|
10129 |
+
if not cluster:
|
10130 |
+
cluster.append(item)
|
10131 |
+
elif abs(item[groupby_idx] - cluster[-1][groupby_idx]) <= threshold:
|
10132 |
+
cluster.append(item)
|
10133 |
+
else:
|
10134 |
+
grouped_data.append(cluster)
|
10135 |
+
cluster = [item]
|
10136 |
+
|
10137 |
+
if cluster:
|
10138 |
+
grouped_data.append(cluster)
|
10139 |
+
|
10140 |
+
return grouped_data
|
10141 |
+
|
10142 |
+
###################################################################################
|
10143 |
+
|
10144 |
+
def split_escore_notes_by_time(escore_notes, time_threshold=256):
|
10145 |
+
|
10146 |
+
dscore = delta_score_notes(escore_notes, timings_clip_value=time_threshold-1)
|
10147 |
+
|
10148 |
+
score_chunks = []
|
10149 |
+
|
10150 |
+
ctime = 0
|
10151 |
+
pchunk_idx = 0
|
10152 |
+
|
10153 |
+
for i, e in enumerate(dscore):
|
10154 |
+
|
10155 |
+
ctime += e[1]
|
10156 |
+
|
10157 |
+
if ctime >= time_threshold:
|
10158 |
+
score_chunks.append(escore_notes[pchunk_idx:i])
|
10159 |
+
pchunk_idx = i
|
10160 |
+
ctime = 0
|
10161 |
+
|
10162 |
+
return score_chunks
|
10163 |
+
|
10164 |
+
###################################################################################
|
10165 |
+
|
10166 |
+
def escore_notes_grouped_patches(escore_notes, time_threshold=256):
|
10167 |
+
|
10168 |
+
split_score_chunks = split_escore_notes_by_time(escore_notes,
|
10169 |
+
time_threshold=time_threshold
|
10170 |
+
)
|
10171 |
+
|
10172 |
+
chunks_patches = []
|
10173 |
+
|
10174 |
+
for s in split_score_chunks:
|
10175 |
+
chunks_patches.append(escore_notes_patches(s))
|
10176 |
+
|
10177 |
+
return chunks_patches
|
10178 |
+
|
10179 |
+
###################################################################################
|
10180 |
+
|
10181 |
+
def computeLPSArray(pattern, M, lps):
|
10182 |
+
length = 0
|
10183 |
+
i = 1
|
10184 |
+
|
10185 |
+
lps[0] = 0
|
10186 |
+
|
10187 |
+
while i < M:
|
10188 |
+
if pattern[i] == pattern[length]:
|
10189 |
+
length += 1
|
10190 |
+
lps[i] = length
|
10191 |
+
i += 1
|
10192 |
+
else:
|
10193 |
+
if length != 0:
|
10194 |
+
length = lps[length-1]
|
10195 |
+
else:
|
10196 |
+
lps[i] = 0
|
10197 |
+
i += 1
|
10198 |
+
|
10199 |
+
###################################################################################
|
10200 |
+
|
10201 |
+
def find_pattern_idxs(sub_pattern, pattern):
|
10202 |
+
|
10203 |
+
lst = pattern
|
10204 |
+
pattern = sub_pattern
|
10205 |
+
|
10206 |
+
M = len(pattern)
|
10207 |
+
N = len(lst)
|
10208 |
+
|
10209 |
+
lps = [0] * M
|
10210 |
+
j = 0 # index for pattern[]
|
10211 |
+
|
10212 |
+
computeLPSArray(pattern, M, lps)
|
10213 |
+
|
10214 |
+
i = 0 # index for lst[]
|
10215 |
+
indexes = []
|
10216 |
+
|
10217 |
+
while i < N:
|
10218 |
+
if pattern[j] == lst[i]:
|
10219 |
+
i += 1
|
10220 |
+
j += 1
|
10221 |
+
|
10222 |
+
if j == M:
|
10223 |
+
end_index = i - 1
|
10224 |
+
start_index = end_index - M + 1
|
10225 |
+
indexes.append((start_index, end_index))
|
10226 |
+
j = lps[j-1]
|
10227 |
+
elif i < N and pattern[j] != lst[i]:
|
10228 |
+
if j != 0:
|
10229 |
+
j = lps[j-1]
|
10230 |
+
else:
|
10231 |
+
i += 1
|
10232 |
+
|
10233 |
+
return indexes
|
10234 |
+
|
10235 |
+
###################################################################################
|
10236 |
+
|
10237 |
+
def escore_notes_patch_lrno_patterns(escore_notes,
|
10238 |
+
patch=0,
|
10239 |
+
zero_score_timings=False,
|
10240 |
+
pitches_idx=4,
|
10241 |
+
patches_idx=6
|
10242 |
+
):
|
10243 |
+
|
10244 |
+
patch_escore = [e for e in escore_notes if e[patches_idx] == patch]
|
10245 |
+
|
10246 |
+
if patch_escore:
|
10247 |
+
|
10248 |
+
patch_cscore = chordify_score([1000, patch_escore])
|
10249 |
+
|
10250 |
+
patch_tscore = []
|
10251 |
+
|
10252 |
+
for c in patch_cscore:
|
10253 |
+
|
10254 |
+
tones_chord = sorted(set([p[pitches_idx] % 12 for p in c]))
|
10255 |
+
|
10256 |
+
if tones_chord not in ALL_CHORDS_SORTED:
|
10257 |
+
tnoes_chord = check_and_fix_tones_chord(tones_chord)
|
10258 |
+
|
10259 |
+
patch_tscore.append(ALL_CHORDS_SORTED.index(tones_chord))
|
10260 |
+
|
10261 |
+
pattern = find_lrno_pattern_fast(patch_tscore)
|
10262 |
+
|
10263 |
+
patterns_idxs = find_pattern_idxs(pattern, patch_tscore)
|
10264 |
+
|
10265 |
+
patch_lrno_scores = []
|
10266 |
+
|
10267 |
+
for idxs in patterns_idxs:
|
10268 |
+
|
10269 |
+
score = patch_escore[idxs[0]:idxs[1]]
|
10270 |
+
|
10271 |
+
if zero_score_timings:
|
10272 |
+
score = recalculate_score_timings(score)
|
10273 |
+
|
10274 |
+
patch_lrno_scores.append(score)
|
10275 |
+
|
10276 |
+
return patch_lrno_scores
|
10277 |
+
|
10278 |
+
else:
|
10279 |
+
return []
|
10280 |
+
|
10281 |
+
###################################################################################
|
10282 |
+
|
10283 |
+
ALL_BASE_CHORDS_SORTED = [[0], [0, 2], [0, 2, 4], [0, 2, 4, 6], [0, 2, 4, 6, 8], [0, 2, 4, 6, 8, 10],
|
10284 |
+
[0, 2, 4, 6, 9], [0, 2, 4, 6, 10], [0, 2, 4, 7], [0, 2, 4, 7, 9],
|
10285 |
+
[0, 2, 4, 7, 10], [0, 2, 4, 8], [0, 2, 4, 8, 10], [0, 2, 4, 9], [0, 2, 4, 10],
|
10286 |
+
[0, 2, 5], [0, 2, 5, 7], [0, 2, 5, 7, 9], [0, 2, 5, 7, 10], [0, 2, 5, 8],
|
10287 |
+
[0, 2, 5, 8, 10], [0, 2, 5, 9], [0, 2, 5, 10], [0, 2, 6], [0, 2, 6, 8],
|
10288 |
+
[0, 2, 6, 8, 10], [0, 2, 6, 9], [0, 2, 6, 10], [0, 2, 7], [0, 2, 7, 9],
|
10289 |
+
[0, 2, 7, 10], [0, 2, 8], [0, 2, 8, 10], [0, 2, 9], [0, 2, 10], [0, 3],
|
10290 |
+
[0, 3, 5], [0, 3, 5, 7], [0, 3, 5, 7, 9], [0, 3, 5, 7, 10], [0, 3, 5, 8],
|
10291 |
+
[0, 3, 5, 8, 10], [0, 3, 5, 9], [0, 3, 5, 10], [0, 3, 6], [0, 3, 6, 8],
|
10292 |
+
[0, 3, 6, 8, 10], [0, 3, 6, 9], [0, 3, 6, 10], [0, 3, 7], [0, 3, 7, 9],
|
10293 |
+
[0, 3, 7, 10], [0, 3, 8], [0, 3, 8, 10], [0, 3, 9], [0, 3, 10], [0, 4],
|
10294 |
+
[0, 4, 6], [0, 4, 6, 8], [0, 4, 6, 8, 10], [0, 4, 6, 9], [0, 4, 6, 10],
|
10295 |
+
[0, 4, 7], [0, 4, 7, 9], [0, 4, 7, 10], [0, 4, 8], [0, 4, 8, 10], [0, 4, 9],
|
10296 |
+
[0, 4, 10], [0, 5], [0, 5, 7], [0, 5, 7, 9], [0, 5, 7, 10], [0, 5, 8],
|
10297 |
+
[0, 5, 8, 10], [0, 5, 9], [0, 5, 10], [0, 6], [0, 6, 8], [0, 6, 8, 10],
|
10298 |
+
[0, 6, 9], [0, 6, 10], [0, 7], [0, 7, 9], [0, 7, 10], [0, 8], [0, 8, 10],
|
10299 |
+
[0, 9], [0, 10]]
|
10300 |
+
|
10301 |
###################################################################################
|
10302 |
#
|
10303 |
# This is the end of the TMIDI X Python module
|