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.
|
@@ -9552,15 +9552,20 @@ MOOD_SCALES = ['Major', 'Minor', 'Major Minor']
|
|
9552 |
###################################################################################
|
9553 |
|
9554 |
def alpha_str(string):
|
9555 |
-
|
|
|
9556 |
|
9557 |
###################################################################################
|
9558 |
|
9559 |
-
def escore_notes_to_text_description(escore_notes,
|
|
|
|
|
|
|
|
|
9560 |
|
9561 |
#==============================================================================
|
9562 |
|
9563 |
-
song_time_min = (escore_notes[-1][1] *
|
9564 |
|
9565 |
if song_time_min < 1.5:
|
9566 |
song_length = 'short'
|
@@ -9592,10 +9597,16 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
|
|
9592 |
|
9593 |
#==============================================================================
|
9594 |
|
9595 |
-
|
|
|
|
|
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 |
|
@@ -9641,22 +9652,22 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
|
|
9641 |
|
9642 |
escore_averages = escore_notes_averages(escore_notes, return_ptcs_and_vels=True)
|
9643 |
|
9644 |
-
if escore_averages[0] <
|
9645 |
rythm = 'fast'
|
9646 |
|
9647 |
-
elif
|
9648 |
rythm = 'average'
|
9649 |
|
9650 |
-
elif escore_averages[0] >
|
9651 |
rythm = 'slow'
|
9652 |
|
9653 |
-
if escore_averages[1] <
|
9654 |
tempo = 'fast'
|
9655 |
|
9656 |
-
elif
|
9657 |
tempo = 'average'
|
9658 |
|
9659 |
-
elif escore_averages[1] >
|
9660 |
tempo = 'slow'
|
9661 |
|
9662 |
if escore_averages[2] < 50:
|
@@ -9740,10 +9751,20 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
|
|
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
|
@@ -10065,6 +10086,239 @@ CONTROL_CHANGES = {
|
|
10065 |
127: "Poly Mode On", # + mono off, +all notes off
|
10066 |
}
|
10067 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10068 |
###################################################################################
|
10069 |
#
|
10070 |
# 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.
|
|
|
9552 |
###################################################################################
|
9553 |
|
9554 |
def alpha_str(string):
|
9555 |
+
astr = re.sub(r'[^a-zA-Z ()]', '', string).strip()
|
9556 |
+
return re.sub(r'\s+', ' ', astr).strip()
|
9557 |
|
9558 |
###################################################################################
|
9559 |
|
9560 |
+
def escore_notes_to_text_description(escore_notes,
|
9561 |
+
song_name='',
|
9562 |
+
artist_name='',
|
9563 |
+
timings_divider=16,
|
9564 |
+
):
|
9565 |
|
9566 |
#==============================================================================
|
9567 |
|
9568 |
+
song_time_min = (escore_notes[-1][1] * timings_divider) / 1000 / 60
|
9569 |
|
9570 |
if song_time_min < 1.5:
|
9571 |
song_length = 'short'
|
|
|
9597 |
|
9598 |
#==============================================================================
|
9599 |
|
9600 |
+
all_patches = [e[6] for e in escore_notes]
|
9601 |
+
|
9602 |
+
patches = ordered_set(all_patches)
|
9603 |
|
9604 |
instruments = [alpha_str(Number2patch[p]) for p in patches if p < 128]
|
9605 |
|
9606 |
+
nd_patches_counts = Counter([p for p in all_patches if p < 128]).most_common()
|
9607 |
+
|
9608 |
+
dominant_instrument = alpha_str(Number2patch[nd_patches_counts[0][0]])
|
9609 |
+
|
9610 |
if 128 in patches:
|
9611 |
drums_present = True
|
9612 |
|
|
|
9652 |
|
9653 |
escore_averages = escore_notes_averages(escore_notes, return_ptcs_and_vels=True)
|
9654 |
|
9655 |
+
if escore_averages[0] < (128 / timings_divider):
|
9656 |
rythm = 'fast'
|
9657 |
|
9658 |
+
elif (128 / timings_divider) <= escore_averages[0] <= (192 / timings_divider):
|
9659 |
rythm = 'average'
|
9660 |
|
9661 |
+
elif escore_averages[0] > (192 / timings_divider):
|
9662 |
rythm = 'slow'
|
9663 |
|
9664 |
+
if escore_averages[1] < (256 / timings_divider):
|
9665 |
tempo = 'fast'
|
9666 |
|
9667 |
+
elif (256 / timings_divider) <= escore_averages[1] <= (384 / timings_divider):
|
9668 |
tempo = 'average'
|
9669 |
|
9670 |
+
elif escore_averages[1] > (384 / timings_divider):
|
9671 |
tempo = 'slow'
|
9672 |
|
9673 |
if escore_averages[2] < 50:
|
|
|
9751 |
|
9752 |
description += '\n'
|
9753 |
|
9754 |
+
description += 'The song starts with ' + instruments[0] + '.'
|
9755 |
+
|
9756 |
+
description += '\n'
|
9757 |
+
|
9758 |
+
description += 'The song main instrument is ' + dominant_instrument + '.'
|
9759 |
+
|
9760 |
+
description += '\n'
|
9761 |
+
|
9762 |
if drums_present and most_common_drums:
|
9763 |
description += 'The drum track has predominant '
|
9764 |
description += ', '.join(most_common_drums[:-1]) + ' and ' + most_common_drums[-1] + '.'
|
9765 |
|
9766 |
+
description += '\n'
|
9767 |
+
|
9768 |
#==============================================================================
|
9769 |
|
9770 |
return description
|
|
|
10086 |
127: "Poly Mode On", # + mono off, +all notes off
|
10087 |
}
|
10088 |
|
10089 |
+
###################################################################################
|
10090 |
+
|
10091 |
+
def patches_onset_times(escore_notes, times_idx=1, patches_idx=6):
|
10092 |
+
|
10093 |
+
patches = [e[patches_idx] for e in escore_notes]
|
10094 |
+
|
10095 |
+
patches_oset = ordered_set(patches)
|
10096 |
+
|
10097 |
+
patches_onset_times = []
|
10098 |
+
|
10099 |
+
for p in patches_oset:
|
10100 |
+
for e in escore_notes:
|
10101 |
+
if e[patches_idx] == p:
|
10102 |
+
patches_onset_times.append([p, e[times_idx]])
|
10103 |
+
break
|
10104 |
+
|
10105 |
+
return patches_onset_times
|
10106 |
+
|
10107 |
+
###################################################################################
|
10108 |
+
|
10109 |
+
def count_escore_notes_patches(escore_notes, patches_idx=6):
|
10110 |
+
|
10111 |
+
patches = [e[patches_idx] for e in escore_notes]
|
10112 |
+
|
10113 |
+
return Counter(patches).most_common()
|
10114 |
+
|
10115 |
+
###################################################################################
|
10116 |
+
|
10117 |
+
def escore_notes_monoponic_melodies(escore_notes,
|
10118 |
+
bad_notes_ratio=0.0,
|
10119 |
+
times_idx=1,
|
10120 |
+
patches_idx=6
|
10121 |
+
):
|
10122 |
+
|
10123 |
+
patches = escore_notes_patches(escore_notes, patches_index=patches_idx)
|
10124 |
+
|
10125 |
+
monophonic_melodies = []
|
10126 |
+
|
10127 |
+
for p in patches:
|
10128 |
+
patch_score = [e for e in escore_notes if e[patches_idx] == p]
|
10129 |
+
|
10130 |
+
ps_times = [e[times_idx] for e in patch_score]
|
10131 |
+
|
10132 |
+
if len(ps_times) <= len(set(ps_times)) * (1+bad_notes_ratio):
|
10133 |
+
monophonic_melodies.append([p, len(patch_score)])
|
10134 |
+
|
10135 |
+
return monophonic_melodies
|
10136 |
+
|
10137 |
+
###################################################################################
|
10138 |
+
|
10139 |
+
from itertools import groupby
|
10140 |
+
from operator import itemgetter
|
10141 |
+
|
10142 |
+
def group_by_threshold(data, threshold, groupby_idx):
|
10143 |
+
|
10144 |
+
data.sort(key=itemgetter(groupby_idx))
|
10145 |
+
|
10146 |
+
grouped_data = []
|
10147 |
+
cluster = []
|
10148 |
+
|
10149 |
+
for i, item in enumerate(data):
|
10150 |
+
if not cluster:
|
10151 |
+
cluster.append(item)
|
10152 |
+
elif abs(item[groupby_idx] - cluster[-1][groupby_idx]) <= threshold:
|
10153 |
+
cluster.append(item)
|
10154 |
+
else:
|
10155 |
+
grouped_data.append(cluster)
|
10156 |
+
cluster = [item]
|
10157 |
+
|
10158 |
+
if cluster:
|
10159 |
+
grouped_data.append(cluster)
|
10160 |
+
|
10161 |
+
return grouped_data
|
10162 |
+
|
10163 |
+
###################################################################################
|
10164 |
+
|
10165 |
+
def split_escore_notes_by_time(escore_notes, time_threshold=256):
|
10166 |
+
|
10167 |
+
dscore = delta_score_notes(escore_notes, timings_clip_value=time_threshold-1)
|
10168 |
+
|
10169 |
+
score_chunks = []
|
10170 |
+
|
10171 |
+
ctime = 0
|
10172 |
+
pchunk_idx = 0
|
10173 |
+
|
10174 |
+
for i, e in enumerate(dscore):
|
10175 |
+
|
10176 |
+
ctime += e[1]
|
10177 |
+
|
10178 |
+
if ctime >= time_threshold:
|
10179 |
+
score_chunks.append(escore_notes[pchunk_idx:i])
|
10180 |
+
pchunk_idx = i
|
10181 |
+
ctime = 0
|
10182 |
+
|
10183 |
+
return score_chunks
|
10184 |
+
|
10185 |
+
###################################################################################
|
10186 |
+
|
10187 |
+
def escore_notes_grouped_patches(escore_notes, time_threshold=256):
|
10188 |
+
|
10189 |
+
split_score_chunks = split_escore_notes_by_time(escore_notes,
|
10190 |
+
time_threshold=time_threshold
|
10191 |
+
)
|
10192 |
+
|
10193 |
+
chunks_patches = []
|
10194 |
+
|
10195 |
+
for s in split_score_chunks:
|
10196 |
+
chunks_patches.append(escore_notes_patches(s))
|
10197 |
+
|
10198 |
+
return chunks_patches
|
10199 |
+
|
10200 |
+
###################################################################################
|
10201 |
+
|
10202 |
+
def computeLPSArray(pattern, M, lps):
|
10203 |
+
length = 0
|
10204 |
+
i = 1
|
10205 |
+
|
10206 |
+
lps[0] = 0
|
10207 |
+
|
10208 |
+
while i < M:
|
10209 |
+
if pattern[i] == pattern[length]:
|
10210 |
+
length += 1
|
10211 |
+
lps[i] = length
|
10212 |
+
i += 1
|
10213 |
+
else:
|
10214 |
+
if length != 0:
|
10215 |
+
length = lps[length-1]
|
10216 |
+
else:
|
10217 |
+
lps[i] = 0
|
10218 |
+
i += 1
|
10219 |
+
|
10220 |
+
###################################################################################
|
10221 |
+
|
10222 |
+
def find_pattern_idxs(sub_pattern, pattern):
|
10223 |
+
|
10224 |
+
lst = pattern
|
10225 |
+
pattern = sub_pattern
|
10226 |
+
|
10227 |
+
M = len(pattern)
|
10228 |
+
N = len(lst)
|
10229 |
+
|
10230 |
+
lps = [0] * M
|
10231 |
+
j = 0 # index for pattern[]
|
10232 |
+
|
10233 |
+
computeLPSArray(pattern, M, lps)
|
10234 |
+
|
10235 |
+
i = 0 # index for lst[]
|
10236 |
+
indexes = []
|
10237 |
+
|
10238 |
+
while i < N:
|
10239 |
+
if pattern[j] == lst[i]:
|
10240 |
+
i += 1
|
10241 |
+
j += 1
|
10242 |
+
|
10243 |
+
if j == M:
|
10244 |
+
end_index = i - 1
|
10245 |
+
start_index = end_index - M + 1
|
10246 |
+
indexes.append((start_index, end_index))
|
10247 |
+
j = lps[j-1]
|
10248 |
+
elif i < N and pattern[j] != lst[i]:
|
10249 |
+
if j != 0:
|
10250 |
+
j = lps[j-1]
|
10251 |
+
else:
|
10252 |
+
i += 1
|
10253 |
+
|
10254 |
+
return indexes
|
10255 |
+
|
10256 |
+
###################################################################################
|
10257 |
+
|
10258 |
+
def escore_notes_patch_lrno_patterns(escore_notes,
|
10259 |
+
patch=0,
|
10260 |
+
zero_score_timings=False,
|
10261 |
+
pitches_idx=4,
|
10262 |
+
patches_idx=6
|
10263 |
+
):
|
10264 |
+
|
10265 |
+
patch_escore = [e for e in escore_notes if e[patches_idx] == patch]
|
10266 |
+
|
10267 |
+
if patch_escore:
|
10268 |
+
|
10269 |
+
patch_cscore = chordify_score([1000, patch_escore])
|
10270 |
+
|
10271 |
+
patch_tscore = []
|
10272 |
+
|
10273 |
+
for c in patch_cscore:
|
10274 |
+
|
10275 |
+
tones_chord = sorted(set([p[pitches_idx] % 12 for p in c]))
|
10276 |
+
|
10277 |
+
if tones_chord not in ALL_CHORDS_SORTED:
|
10278 |
+
tnoes_chord = check_and_fix_tones_chord(tones_chord)
|
10279 |
+
|
10280 |
+
patch_tscore.append(ALL_CHORDS_SORTED.index(tones_chord))
|
10281 |
+
|
10282 |
+
pattern = find_lrno_pattern_fast(patch_tscore)
|
10283 |
+
|
10284 |
+
patterns_idxs = find_pattern_idxs(pattern, patch_tscore)
|
10285 |
+
|
10286 |
+
patch_lrno_scores = []
|
10287 |
+
|
10288 |
+
for idxs in patterns_idxs:
|
10289 |
+
|
10290 |
+
score = patch_escore[idxs[0]:idxs[1]]
|
10291 |
+
|
10292 |
+
if zero_score_timings:
|
10293 |
+
score = recalculate_score_timings(score)
|
10294 |
+
|
10295 |
+
patch_lrno_scores.append(score)
|
10296 |
+
|
10297 |
+
return patch_lrno_scores
|
10298 |
+
|
10299 |
+
else:
|
10300 |
+
return []
|
10301 |
+
|
10302 |
+
###################################################################################
|
10303 |
+
|
10304 |
+
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],
|
10305 |
+
[0, 2, 4, 6, 9], [0, 2, 4, 6, 10], [0, 2, 4, 7], [0, 2, 4, 7, 9],
|
10306 |
+
[0, 2, 4, 7, 10], [0, 2, 4, 8], [0, 2, 4, 8, 10], [0, 2, 4, 9], [0, 2, 4, 10],
|
10307 |
+
[0, 2, 5], [0, 2, 5, 7], [0, 2, 5, 7, 9], [0, 2, 5, 7, 10], [0, 2, 5, 8],
|
10308 |
+
[0, 2, 5, 8, 10], [0, 2, 5, 9], [0, 2, 5, 10], [0, 2, 6], [0, 2, 6, 8],
|
10309 |
+
[0, 2, 6, 8, 10], [0, 2, 6, 9], [0, 2, 6, 10], [0, 2, 7], [0, 2, 7, 9],
|
10310 |
+
[0, 2, 7, 10], [0, 2, 8], [0, 2, 8, 10], [0, 2, 9], [0, 2, 10], [0, 3],
|
10311 |
+
[0, 3, 5], [0, 3, 5, 7], [0, 3, 5, 7, 9], [0, 3, 5, 7, 10], [0, 3, 5, 8],
|
10312 |
+
[0, 3, 5, 8, 10], [0, 3, 5, 9], [0, 3, 5, 10], [0, 3, 6], [0, 3, 6, 8],
|
10313 |
+
[0, 3, 6, 8, 10], [0, 3, 6, 9], [0, 3, 6, 10], [0, 3, 7], [0, 3, 7, 9],
|
10314 |
+
[0, 3, 7, 10], [0, 3, 8], [0, 3, 8, 10], [0, 3, 9], [0, 3, 10], [0, 4],
|
10315 |
+
[0, 4, 6], [0, 4, 6, 8], [0, 4, 6, 8, 10], [0, 4, 6, 9], [0, 4, 6, 10],
|
10316 |
+
[0, 4, 7], [0, 4, 7, 9], [0, 4, 7, 10], [0, 4, 8], [0, 4, 8, 10], [0, 4, 9],
|
10317 |
+
[0, 4, 10], [0, 5], [0, 5, 7], [0, 5, 7, 9], [0, 5, 7, 10], [0, 5, 8],
|
10318 |
+
[0, 5, 8, 10], [0, 5, 9], [0, 5, 10], [0, 6], [0, 6, 8], [0, 6, 8, 10],
|
10319 |
+
[0, 6, 9], [0, 6, 10], [0, 7], [0, 7, 9], [0, 7, 10], [0, 8], [0, 8, 10],
|
10320 |
+
[0, 9], [0, 10]]
|
10321 |
+
|
10322 |
###################################################################################
|
10323 |
#
|
10324 |
# This is the end of the TMIDI X Python module
|