Upload TMIDIX.py
Browse files
TMIDIX.py
CHANGED
|
@@ -1522,6 +1522,8 @@ from array import array
|
|
| 1522 |
from pathlib import Path
|
| 1523 |
from fnmatch import fnmatch
|
| 1524 |
|
|
|
|
|
|
|
| 1525 |
###################################################################################
|
| 1526 |
#
|
| 1527 |
# Original TMIDI Tegridy helper functions
|
|
@@ -15168,6 +15170,452 @@ def chords_to_escore_notes(chords,
|
|
| 15168 |
|
| 15169 |
###################################################################################
|
| 15170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15171 |
print('Module loaded!')
|
| 15172 |
print('=' * 70)
|
| 15173 |
print('Enjoy! :)')
|
|
|
|
| 1522 |
from pathlib import Path
|
| 1523 |
from fnmatch import fnmatch
|
| 1524 |
|
| 1525 |
+
from typing import List, Optional
|
| 1526 |
+
|
| 1527 |
###################################################################################
|
| 1528 |
#
|
| 1529 |
# Original TMIDI Tegridy helper functions
|
|
|
|
| 15170 |
|
| 15171 |
###################################################################################
|
| 15172 |
|
| 15173 |
+
def _median(vals: List[float]) -> float:
|
| 15174 |
+
return statistics.median(vals) if vals else 0.0
|
| 15175 |
+
|
| 15176 |
+
###################################################################################
|
| 15177 |
+
|
| 15178 |
+
def _mad(vals: List[float], center: Optional[float] = None) -> float:
|
| 15179 |
+
if not vals:
|
| 15180 |
+
return 0.0
|
| 15181 |
+
if center is None:
|
| 15182 |
+
center = _median(vals)
|
| 15183 |
+
return _median([abs(x - center) for x in vals])
|
| 15184 |
+
|
| 15185 |
+
###################################################################################
|
| 15186 |
+
|
| 15187 |
+
def _rep(vals: List[float], method: str) -> float:
|
| 15188 |
+
if method == "mean":
|
| 15189 |
+
return statistics.mean(vals)
|
| 15190 |
+
if method == "mode":
|
| 15191 |
+
try:
|
| 15192 |
+
return statistics.mode(vals)
|
| 15193 |
+
except statistics.StatisticsError:
|
| 15194 |
+
return statistics.median(vals)
|
| 15195 |
+
return statistics.median(vals)
|
| 15196 |
+
|
| 15197 |
+
###################################################################################
|
| 15198 |
+
|
| 15199 |
+
def _round_int(v: float, mode: Optional[str]) -> int:
|
| 15200 |
+
if mode is None or mode == "round":
|
| 15201 |
+
return int(round(v))
|
| 15202 |
+
if mode == "ceil":
|
| 15203 |
+
return int(math.ceil(v))
|
| 15204 |
+
if mode == "floor":
|
| 15205 |
+
return int(math.floor(v))
|
| 15206 |
+
raise ValueError("Unknown round mode")
|
| 15207 |
+
|
| 15208 |
+
###################################################################################
|
| 15209 |
+
|
| 15210 |
+
def even_out_values_in_list_of_lists(
|
| 15211 |
+
data: List[List[float]],
|
| 15212 |
+
rep_method: str = "median",
|
| 15213 |
+
z_thresh: float = 3.0,
|
| 15214 |
+
local_ratio_thresh: float = 3.0,
|
| 15215 |
+
gap_ratio_thresh: float = 0.45,
|
| 15216 |
+
merge_factor: float = 1.0,
|
| 15217 |
+
span_tol_factor: float = 1.0,
|
| 15218 |
+
min_cluster_size: int = 1,
|
| 15219 |
+
round_mode: Optional[str] = "round",
|
| 15220 |
+
max_k_for_kmeans: int = 5
|
| 15221 |
+
) -> List[List[int]]:
|
| 15222 |
+
|
| 15223 |
+
# small deterministic 1D kmeans for fallback
|
| 15224 |
+
def _kmeans_1d(values, k, rng_seed=42, max_iter=100):
|
| 15225 |
+
n = len(values)
|
| 15226 |
+
if k <= 1 or k >= n:
|
| 15227 |
+
return [statistics.mean(values)]*k if k>0 else [], list(range(n))
|
| 15228 |
+
rng = random.Random(rng_seed)
|
| 15229 |
+
centroids = [rng.choice(values)]
|
| 15230 |
+
for _ in range(1, k):
|
| 15231 |
+
d2 = [min((v - c)**2 for c in centroids) for v in values]
|
| 15232 |
+
total = sum(d2)
|
| 15233 |
+
if total == 0:
|
| 15234 |
+
centroids.append(rng.choice(values))
|
| 15235 |
+
continue
|
| 15236 |
+
r = rng.random() * total
|
| 15237 |
+
cum = 0.0
|
| 15238 |
+
for v, w in zip(values, d2):
|
| 15239 |
+
cum += w
|
| 15240 |
+
if cum >= r:
|
| 15241 |
+
centroids.append(v)
|
| 15242 |
+
break
|
| 15243 |
+
labels = [0]*n
|
| 15244 |
+
for _ in range(max_iter):
|
| 15245 |
+
changed = False
|
| 15246 |
+
for i, v in enumerate(values):
|
| 15247 |
+
best = min(range(k), key=lambda j: abs(v - centroids[j]))
|
| 15248 |
+
if labels[i] != best:
|
| 15249 |
+
labels[i] = best
|
| 15250 |
+
changed = True
|
| 15251 |
+
new_centroids = []
|
| 15252 |
+
for j in range(k):
|
| 15253 |
+
members = [v for v, lab in zip(values, labels) if lab == j]
|
| 15254 |
+
new_centroids.append(sum(members)/len(members) if members else rng.choice(values))
|
| 15255 |
+
if all(abs(a-b) < 1e-12 for a,b in zip(centroids, new_centroids)):
|
| 15256 |
+
break
|
| 15257 |
+
centroids = new_centroids
|
| 15258 |
+
if not changed:
|
| 15259 |
+
break
|
| 15260 |
+
return centroids, labels
|
| 15261 |
+
|
| 15262 |
+
def _silhouette_1d(values, labels):
|
| 15263 |
+
n = len(values)
|
| 15264 |
+
if n <= 1:
|
| 15265 |
+
return 0.0
|
| 15266 |
+
clusters = {}
|
| 15267 |
+
for v, lab in zip(values, labels):
|
| 15268 |
+
clusters.setdefault(lab, []).append(v)
|
| 15269 |
+
if len(clusters) == 1:
|
| 15270 |
+
return 0.0
|
| 15271 |
+
total_s = 0.0
|
| 15272 |
+
for i, v in enumerate(values):
|
| 15273 |
+
lab = labels[i]
|
| 15274 |
+
own = clusters[lab]
|
| 15275 |
+
a = 0.0
|
| 15276 |
+
if len(own) > 1:
|
| 15277 |
+
a = sum(abs(v - u) for u in own if u is not v) / (len(own)-1)
|
| 15278 |
+
b = float("inf")
|
| 15279 |
+
for other_lab, members in clusters.items():
|
| 15280 |
+
if other_lab == lab:
|
| 15281 |
+
continue
|
| 15282 |
+
dist = sum(abs(v - u) for u in members) / len(members)
|
| 15283 |
+
if dist < b:
|
| 15284 |
+
b = dist
|
| 15285 |
+
denom = max(a, b)
|
| 15286 |
+
s = 0.0 if denom == 0 else (b - a) / denom
|
| 15287 |
+
total_s += s
|
| 15288 |
+
return total_s / n
|
| 15289 |
+
|
| 15290 |
+
out: List[List[int]] = []
|
| 15291 |
+
|
| 15292 |
+
for row in data:
|
| 15293 |
+
if not row:
|
| 15294 |
+
out.append([])
|
| 15295 |
+
continue
|
| 15296 |
+
if len(row) == 1:
|
| 15297 |
+
out.append([_round_int(float(row[0]), round_mode)])
|
| 15298 |
+
continue
|
| 15299 |
+
|
| 15300 |
+
indexed = sorted(((float(v), idx) for idx, v in enumerate(row)), key=lambda x: x[0])
|
| 15301 |
+
sorted_vals = [v for v, _ in indexed]
|
| 15302 |
+
n = len(sorted_vals)
|
| 15303 |
+
|
| 15304 |
+
if all(v == sorted_vals[0] for v in sorted_vals):
|
| 15305 |
+
rep_int = _round_int(_rep(sorted_vals, rep_method), round_mode)
|
| 15306 |
+
out.append([rep_int]*n)
|
| 15307 |
+
continue
|
| 15308 |
+
|
| 15309 |
+
# adjacent diffs and robust stats
|
| 15310 |
+
diffs = [sorted_vals[i+1] - sorted_vals[i] for i in range(n-1)]
|
| 15311 |
+
med = _median(diffs)
|
| 15312 |
+
mad = _mad(diffs, med)
|
| 15313 |
+
z_scores = [(d - med) / (mad + 1e-12) for d in diffs]
|
| 15314 |
+
|
| 15315 |
+
# initial cut heuristics
|
| 15316 |
+
cuts = set(idx for idx, z in enumerate(z_scores) if z > z_thresh)
|
| 15317 |
+
if diffs:
|
| 15318 |
+
# percentile fallback
|
| 15319 |
+
k_idx = max(0, min(len(diffs)-1, int(round(len(diffs)*0.9)) - 1))
|
| 15320 |
+
pcut = sorted(diffs)[k_idx]
|
| 15321 |
+
cuts.update(idx for idx, d in enumerate(diffs) if d >= pcut and d > 0)
|
| 15322 |
+
# global gap ratio
|
| 15323 |
+
maxd = max(diffs)
|
| 15324 |
+
cuts.update(idx for idx, d in enumerate(diffs) if d >= maxd * gap_ratio_thresh and d > 0)
|
| 15325 |
+
# local relative gap
|
| 15326 |
+
for i, d in enumerate(diffs):
|
| 15327 |
+
neigh = []
|
| 15328 |
+
for j in range(i-2, i+3):
|
| 15329 |
+
if j != i and 0 <= j < len(diffs):
|
| 15330 |
+
neigh.append(diffs[j])
|
| 15331 |
+
if not neigh:
|
| 15332 |
+
continue
|
| 15333 |
+
local_med = _median(neigh)
|
| 15334 |
+
denom = local_med if local_med > 1e-12 else (med if med > 1e-12 else 1.0)
|
| 15335 |
+
if d / denom >= local_ratio_thresh and d > 0:
|
| 15336 |
+
cuts.add(i)
|
| 15337 |
+
|
| 15338 |
+
# build clusters from cuts
|
| 15339 |
+
if not cuts:
|
| 15340 |
+
clusters = [sorted_vals[:]]
|
| 15341 |
+
else:
|
| 15342 |
+
clusters = []
|
| 15343 |
+
start = 0
|
| 15344 |
+
for i in range(len(diffs)):
|
| 15345 |
+
if i in cuts:
|
| 15346 |
+
clusters.append(sorted_vals[start:i+1])
|
| 15347 |
+
start = i+1
|
| 15348 |
+
clusters.append(sorted_vals[start:])
|
| 15349 |
+
|
| 15350 |
+
# --- Merge Logic (Extracted) ---
|
| 15351 |
+
def _do_merges(clusters_in):
|
| 15352 |
+
# compute representatives and cluster spans
|
| 15353 |
+
reps = [_rep(cl, rep_method) for cl in clusters_in]
|
| 15354 |
+
spans = [ (max(cl)-min(cl)) if len(cl)>1 else 0.0 for cl in clusters_in ]
|
| 15355 |
+
|
| 15356 |
+
# merge-adjacent rule
|
| 15357 |
+
row_mad = _mad(sorted_vals, _median(sorted_vals))
|
| 15358 |
+
merge_tol = max(1.0, merge_factor * row_mad)
|
| 15359 |
+
|
| 15360 |
+
merged = []
|
| 15361 |
+
merged_reps = []
|
| 15362 |
+
merged_spans = []
|
| 15363 |
+
k = 0
|
| 15364 |
+
while k < len(clusters_in):
|
| 15365 |
+
cur = clusters_in[k][:]
|
| 15366 |
+
cur_rep = reps[k]
|
| 15367 |
+
cur_span = spans[k]
|
| 15368 |
+
# Merge adjacent clusters if reps are close
|
| 15369 |
+
while k+1 < len(clusters_in) and abs(cur_rep - reps[k+1]) <= merge_tol:
|
| 15370 |
+
cur += clusters_in[k+1]
|
| 15371 |
+
cur_rep = _rep(cur, rep_method)
|
| 15372 |
+
cur_span = max(cur_span, spans[k+1], max(cur)-min(cur))
|
| 15373 |
+
k += 1
|
| 15374 |
+
merged.append(cur)
|
| 15375 |
+
merged_reps.append(cur_rep)
|
| 15376 |
+
merged_spans.append(cur_span)
|
| 15377 |
+
k += 1
|
| 15378 |
+
|
| 15379 |
+
current_clusters = merged
|
| 15380 |
+
current_reps = merged_reps
|
| 15381 |
+
current_spans = merged_spans
|
| 15382 |
+
|
| 15383 |
+
# span-based merge
|
| 15384 |
+
span_tol = max(1.0, span_tol_factor * row_mad)
|
| 15385 |
+
if len(current_clusters) > 1:
|
| 15386 |
+
i = 0
|
| 15387 |
+
while i < len(current_clusters):
|
| 15388 |
+
if len(current_clusters[i]) < min_cluster_size or current_spans[i] <= span_tol:
|
| 15389 |
+
left = i-1 if i-1 >= 0 else None
|
| 15390 |
+
right = i+1 if i+1 < len(current_clusters) else None
|
| 15391 |
+
|
| 15392 |
+
merged_flag = False
|
| 15393 |
+
# Try merge right
|
| 15394 |
+
if left is None and right is not None:
|
| 15395 |
+
if abs(current_reps[i] - current_reps[right]) <= merge_tol:
|
| 15396 |
+
current_clusters[right] = current_clusters[i] + current_clusters[right]
|
| 15397 |
+
del current_clusters[i]
|
| 15398 |
+
current_reps = [_rep(cl, rep_method) for cl in current_clusters]
|
| 15399 |
+
current_spans = [ (max(cl)-min(cl)) if len(cl)>1 else 0.0 for cl in current_clusters]
|
| 15400 |
+
merged_flag = True
|
| 15401 |
+
# Try merge left
|
| 15402 |
+
elif right is None and left is not None:
|
| 15403 |
+
if abs(current_reps[i] - current_reps[left]) <= merge_tol:
|
| 15404 |
+
current_clusters[left] = current_clusters[left] + current_clusters[i]
|
| 15405 |
+
del current_clusters[i]
|
| 15406 |
+
current_reps = [_rep(cl, rep_method) for cl in current_clusters]
|
| 15407 |
+
current_spans = [ (max(cl)-min(cl)) if len(cl)>1 else 0.0 for cl in current_clusters]
|
| 15408 |
+
merged_flag = True
|
| 15409 |
+
i -= 1 # Adjust index since we removed an element before i
|
| 15410 |
+
# Try merge closest neighbor
|
| 15411 |
+
elif left is not None and right is not None:
|
| 15412 |
+
dist_left = abs(current_reps[i] - current_reps[left])
|
| 15413 |
+
dist_right = abs(current_reps[i] - current_reps[right])
|
| 15414 |
+
|
| 15415 |
+
target = -1
|
| 15416 |
+
if dist_left <= dist_right:
|
| 15417 |
+
if dist_left <= merge_tol:
|
| 15418 |
+
target = left
|
| 15419 |
+
else:
|
| 15420 |
+
if dist_right <= merge_tol:
|
| 15421 |
+
target = right
|
| 15422 |
+
|
| 15423 |
+
if target != -1:
|
| 15424 |
+
if target == left:
|
| 15425 |
+
current_clusters[left] = current_clusters[left] + current_clusters[i]
|
| 15426 |
+
del current_clusters[i]
|
| 15427 |
+
merged_flag = True
|
| 15428 |
+
i -= 1
|
| 15429 |
+
else: # target == right
|
| 15430 |
+
current_clusters[right] = current_clusters[i] + current_clusters[right]
|
| 15431 |
+
del current_clusters[i]
|
| 15432 |
+
merged_flag = True
|
| 15433 |
+
|
| 15434 |
+
current_reps = [_rep(cl, rep_method) for cl in current_clusters]
|
| 15435 |
+
current_spans = [ (max(cl)-min(cl)) if len(cl)>1 else 0.0 for cl in current_clusters]
|
| 15436 |
+
|
| 15437 |
+
if not merged_flag:
|
| 15438 |
+
i += 1
|
| 15439 |
+
else:
|
| 15440 |
+
i += 1
|
| 15441 |
+
return current_clusters
|
| 15442 |
+
|
| 15443 |
+
# Apply merges to initial heuristic clusters
|
| 15444 |
+
clusters = _do_merges(clusters)
|
| 15445 |
+
|
| 15446 |
+
# final fallback: kmeans
|
| 15447 |
+
# Check condition again because merges might have reduced cluster count
|
| 15448 |
+
if len(clusters) <= 2 and n >= 4:
|
| 15449 |
+
best_score = -1.0
|
| 15450 |
+
best_labels = None
|
| 15451 |
+
best_k = 1
|
| 15452 |
+
max_k = min(max_k_for_kmeans, n)
|
| 15453 |
+
for k_try in range(1, max_k+1):
|
| 15454 |
+
_, labels = _kmeans_1d(sorted_vals, k_try)
|
| 15455 |
+
score = 0.0 if k_try==1 else _silhouette_1d(sorted_vals, labels)
|
| 15456 |
+
if score > best_score + 1e-12:
|
| 15457 |
+
best_score = score
|
| 15458 |
+
best_labels = labels[:]
|
| 15459 |
+
best_k = k_try
|
| 15460 |
+
|
| 15461 |
+
if best_labels is not None and best_k > 1 and best_score > 0:
|
| 15462 |
+
clusters_k = []
|
| 15463 |
+
cur_lab = best_labels[0]
|
| 15464 |
+
cur_cl = [sorted_vals[0]]
|
| 15465 |
+
for v, lab in zip(sorted_vals[1:], best_labels[1:]):
|
| 15466 |
+
if lab == cur_lab:
|
| 15467 |
+
cur_cl.append(v)
|
| 15468 |
+
else:
|
| 15469 |
+
clusters_k.append(cur_cl)
|
| 15470 |
+
cur_cl = [v]
|
| 15471 |
+
cur_lab = lab
|
| 15472 |
+
clusters_k.append(cur_cl)
|
| 15473 |
+
|
| 15474 |
+
# Only replace if Kmeans found more clusters than we currently have
|
| 15475 |
+
# AND crucially, run the merge logic on these new clusters
|
| 15476 |
+
if len(clusters_k) > len(clusters):
|
| 15477 |
+
clusters = _do_merges(clusters_k)
|
| 15478 |
+
|
| 15479 |
+
# compute final reps and round to ints, map back to original order
|
| 15480 |
+
final_reps = [_rep(cl, rep_method) for cl in clusters]
|
| 15481 |
+
rep_values_sorted = []
|
| 15482 |
+
for rep, cl in zip(final_reps, clusters):
|
| 15483 |
+
rep_int = _round_int(rep, round_mode)
|
| 15484 |
+
rep_values_sorted.extend([rep_int] * len(cl))
|
| 15485 |
+
|
| 15486 |
+
row_ints = [0]*n
|
| 15487 |
+
for (_, orig_idx), rep_int in zip(indexed, rep_values_sorted):
|
| 15488 |
+
row_ints[orig_idx] = rep_int
|
| 15489 |
+
|
| 15490 |
+
out.append(row_ints)
|
| 15491 |
+
|
| 15492 |
+
return out
|
| 15493 |
+
|
| 15494 |
+
###################################################################################
|
| 15495 |
+
|
| 15496 |
+
def even_out_durations_in_escore_notes(escore_notes, min_notes_gap=0):
|
| 15497 |
+
|
| 15498 |
+
escore_notes = fix_escore_notes_durations(escore_notes,
|
| 15499 |
+
min_notes_gap=min_notes_gap
|
| 15500 |
+
)
|
| 15501 |
+
|
| 15502 |
+
score = [e for e in escore_notes if e[3] != 9]
|
| 15503 |
+
drums = [e for e in escore_notes if e[3] == 9]
|
| 15504 |
+
|
| 15505 |
+
cscore = chordify_score([1000, score])
|
| 15506 |
+
|
| 15507 |
+
durs = []
|
| 15508 |
+
|
| 15509 |
+
for c in cscore:
|
| 15510 |
+
durs.append([e[2] for e in c])
|
| 15511 |
+
|
| 15512 |
+
ndurs = even_out_values_in_list_of_lists(durs)
|
| 15513 |
+
|
| 15514 |
+
output_score = []
|
| 15515 |
+
|
| 15516 |
+
for i, c in enumerate(cscore):
|
| 15517 |
+
cc = copy.deepcopy(c)
|
| 15518 |
+
ndur = ndurs[i]
|
| 15519 |
+
|
| 15520 |
+
for j, e in enumerate(cc):
|
| 15521 |
+
e[2] = ndur[j]
|
| 15522 |
+
|
| 15523 |
+
output_score.extend(cc)
|
| 15524 |
+
|
| 15525 |
+
return sorted(output_score + drums, key=lambda x: (x[1], -x[4]))
|
| 15526 |
+
|
| 15527 |
+
###################################################################################
|
| 15528 |
+
|
| 15529 |
+
def even_out_velocities_in_escore_notes(escore_notes, use_pitches=False):
|
| 15530 |
+
|
| 15531 |
+
score = [e for e in escore_notes if e[3] != 9]
|
| 15532 |
+
drums = [e for e in escore_notes if e[3] == 9]
|
| 15533 |
+
|
| 15534 |
+
cscore = chordify_score([1000, score])
|
| 15535 |
+
|
| 15536 |
+
vels = []
|
| 15537 |
+
|
| 15538 |
+
for c in cscore:
|
| 15539 |
+
if use_pitches:
|
| 15540 |
+
vels.append([max(40, e[4]) for e in c])
|
| 15541 |
+
|
| 15542 |
+
else:
|
| 15543 |
+
vels.append([e[5] for e in c])
|
| 15544 |
+
|
| 15545 |
+
nvels = even_out_values_in_list_of_lists(vels)
|
| 15546 |
+
|
| 15547 |
+
output_score = []
|
| 15548 |
+
|
| 15549 |
+
for i, c in enumerate(cscore):
|
| 15550 |
+
cc = copy.deepcopy(c)
|
| 15551 |
+
nvel = nvels[i]
|
| 15552 |
+
|
| 15553 |
+
for j, e in enumerate(cc):
|
| 15554 |
+
e[5] = nvel[j]
|
| 15555 |
+
|
| 15556 |
+
output_score.extend(cc)
|
| 15557 |
+
|
| 15558 |
+
return sorted(output_score + drums, key=lambda x: (x[1], -x[4]))
|
| 15559 |
+
|
| 15560 |
+
###################################################################################
|
| 15561 |
+
|
| 15562 |
+
# Most probable MIDI velocity per GM instrument (0-127) + drums (128)
|
| 15563 |
+
INSTRUMENTS_VELOCITIES_MAP = {
|
| 15564 |
+
|
| 15565 |
+
# 0β7 Pianos
|
| 15566 |
+
0: 82, 1: 82, 2: 84, 3: 84, 4: 86, 5: 86, 6: 88, 7: 88,
|
| 15567 |
+
|
| 15568 |
+
# 8β15 Chromatic Percussion
|
| 15569 |
+
8: 78, 9: 78, 10: 80, 11: 80, 12: 82, 13: 82, 14: 84, 15: 84,
|
| 15570 |
+
|
| 15571 |
+
# 16β23 Organs
|
| 15572 |
+
16: 76, 17: 76, 18: 78, 19: 78, 20: 78, 21: 78, 22: 80, 23: 80,
|
| 15573 |
+
|
| 15574 |
+
# 24β31 Guitars
|
| 15575 |
+
24: 80, 25: 80, 26: 82, 27: 82, 28: 84, 29: 84, 30: 86, 31: 86,
|
| 15576 |
+
|
| 15577 |
+
# 32β39 Basses
|
| 15578 |
+
32: 84, 33: 84, 34: 86, 35: 86, 36: 88, 37: 88, 38: 90, 39: 90,
|
| 15579 |
+
|
| 15580 |
+
# 40β47 Strings
|
| 15581 |
+
40: 74, 41: 74, 42: 74, 43: 74, 44: 76, 45: 76, 46: 76, 47: 76,
|
| 15582 |
+
|
| 15583 |
+
# 48β55 Ensemble
|
| 15584 |
+
48: 76, 49: 76, 50: 78, 51: 78, 52: 78, 53: 78, 54: 80, 55: 80,
|
| 15585 |
+
|
| 15586 |
+
# 56β63 Brass
|
| 15587 |
+
56: 86, 57: 86, 58: 88, 59: 88, 60: 90, 61: 90, 62: 92, 63: 92,
|
| 15588 |
+
|
| 15589 |
+
# 64β71 Reed
|
| 15590 |
+
64: 74, 65: 74, 66: 76, 67: 76, 68: 76, 69: 76, 70: 78, 71: 78,
|
| 15591 |
+
|
| 15592 |
+
# 72β79 Pipe
|
| 15593 |
+
72: 72, 73: 72, 74: 74, 75: 74, 76: 74, 77: 74, 78: 76, 79: 76,
|
| 15594 |
+
|
| 15595 |
+
# 80β87 Synth Lead
|
| 15596 |
+
80: 88, 81: 88, 82: 90, 83: 90, 84: 90, 85: 90, 86: 92, 87: 92,
|
| 15597 |
+
|
| 15598 |
+
# 88β95 Synth Pad
|
| 15599 |
+
88: 70, 89: 70, 90: 72, 91: 72, 92: 74, 93: 74, 94: 74, 95: 74,
|
| 15600 |
+
|
| 15601 |
+
# 96β103 Synth Effects
|
| 15602 |
+
96: 76, 97: 76, 98: 78, 99: 78, 100: 80, 101: 80, 102: 82, 103: 82,
|
| 15603 |
+
|
| 15604 |
+
# 104β111 Ethnic
|
| 15605 |
+
104: 74, 105: 74, 106: 76, 107: 76, 108: 76, 109: 76, 110: 78, 111: 78,
|
| 15606 |
+
|
| 15607 |
+
# 112β119 Percussive
|
| 15608 |
+
112: 86, 113: 86, 114: 88, 115: 88, 116: 88, 117: 88, 118: 90, 119: 90,
|
| 15609 |
+
|
| 15610 |
+
# 120β127 Sound Effects
|
| 15611 |
+
120: 72, 121: 72, 122: 74, 123: 74, 124: 74, 125: 74, 126: 76, 127: 76,
|
| 15612 |
+
|
| 15613 |
+
# 128 Drums (GM Channel 10)
|
| 15614 |
+
128: 92,
|
| 15615 |
+
}
|
| 15616 |
+
|
| 15617 |
+
###################################################################################
|
| 15618 |
+
|
| 15619 |
print('Module loaded!')
|
| 15620 |
print('=' * 70)
|
| 15621 |
print('Enjoy! :)')
|