asigalov61 commited on
Commit
ab15083
·
verified ·
1 Parent(s): b472454

Upload TMIDIX.py

Browse files
Files changed (1) hide show
  1. TMIDIX.py +281 -1
TMIDIX.py CHANGED
@@ -48,7 +48,7 @@ r'''
48
 
49
  ###################################################################################
50
 
51
- __version__ = "26.5.18" # TMIDIX version
52
 
53
  ###################################################################################
54
 
@@ -18964,6 +18964,286 @@ def remove_repeating_patterns(lst: list[int]) -> list[int]:
18964
 
18965
  ###################################################################################
18966
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18967
  print('Module loaded!')
18968
  print('=' * 70)
18969
  print('Enjoy! :)')
 
48
 
49
  ###################################################################################
50
 
51
+ __version__ = "26.5.19" # TMIDIX version
52
 
53
  ###################################################################################
54
 
 
18964
 
18965
  ###################################################################################
18966
 
18967
+ def find_repeating_non_overlapping_patterns(arr, min_len):
18968
+ """
18969
+ Finds all repeating non-overlapping patterns of min_len and longer.
18970
+ Excludes overlapping subpatterns from longest to shortest.
18971
+
18972
+ :param arr: List[int] - The input sequence
18973
+ :param min_len: int - The minimum length of patterns
18974
+ :return: Dict[Tuple[int, ...], int] - Dictionary of patterns and their valid counts
18975
+ """
18976
+ n = len(arr)
18977
+ if n < min_len * 2:
18978
+ return {}
18979
+
18980
+ # Tracks indices consumed by longer patterns to exclude shorter overlapping subpatterns
18981
+ consumed = [False] * n
18982
+
18983
+ # Max possible repeating length is half the array
18984
+ max_len = n // 2
18985
+
18986
+ result = {}
18987
+
18988
+ for L in range(max_len, min_len - 1, -1):
18989
+ # 1. Group all starting indices by their pattern tuple
18990
+ groups = {}
18991
+ for i in range(n - L + 1):
18992
+ pat = tuple(arr[i:i + L])
18993
+ if pat in groups:
18994
+ groups[pat].append(i)
18995
+ else:
18996
+ groups[pat] = [i]
18997
+
18998
+ # 2. Process each pattern group
18999
+ for pat, indices in groups.items():
19000
+ count = 0
19001
+ last_end = -1
19002
+
19003
+ # Sort indices to process left-to-right
19004
+ indices.sort()
19005
+
19006
+ for i in indices:
19007
+ # Skip if this occurrence is inside an already consumed longer pattern
19008
+ if consumed[i]:
19009
+ continue
19010
+
19011
+ # If it doesn't overlap with the previous selected occurrence of THIS pattern
19012
+ if i >= last_end:
19013
+ count += 1
19014
+ last_end = i + L
19015
+
19016
+ # 3. If we found repeating occurrences, record it and consume the indices
19017
+ if count >= 2:
19018
+ result[pat] = count
19019
+
19020
+ # We must re-iterate to mark the exact consumed indices
19021
+ last_end = -1
19022
+ for i in indices:
19023
+ if consumed[i]:
19024
+ continue
19025
+ if i >= last_end:
19026
+ # Mark these indices as consumed for shorter patterns
19027
+ for k in range(i, i + L):
19028
+ consumed[k] = True
19029
+ last_end = i + L
19030
+
19031
+ return result
19032
+
19033
+ ###################################################################################
19034
+
19035
+ def get_chord_name(intervals):
19036
+ if not intervals:
19037
+ return "Unknown"
19038
+
19039
+ ivs = sorted(intervals)
19040
+ root = ivs[0]
19041
+
19042
+ # Standard note names for roots 0 through 11
19043
+ root_names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
19044
+ R = root_names[root]
19045
+
19046
+ # 1. Single note
19047
+ if len(ivs) == 1:
19048
+ return R
19049
+
19050
+ # Normalize intervals relative to the root (so root always acts as 0)
19051
+ norm_ivs = sorted([i - root for i in ivs])
19052
+
19053
+ # 2. Dyads (Intervals / Power Chords)
19054
+ if len(norm_ivs) == 2:
19055
+ if norm_ivs[1] == 1: return f"{R}(addb9)"
19056
+ if norm_ivs[1] == 2: return f"{R}(add9)"
19057
+ if norm_ivs[1] == 3: return f"{R}m" # minor 3rd implies minor chord
19058
+ if norm_ivs[1] == 4: return f"{R}" # major 3rd implies major chord
19059
+ if norm_ivs[1] == 5: return f"{R}(add11)" # perfect 4th implies add11
19060
+ if norm_ivs[1] == 6: return f"{R}(b5)"
19061
+ if norm_ivs[1] == 7: return f"{R}5"
19062
+ if norm_ivs[1] == 8: return f"{R}(#5)"
19063
+ if norm_ivs[1] == 9: return f"{R}6"
19064
+ if norm_ivs[1] == 10: return f"{R}(m7)"
19065
+ if norm_ivs[1] == 11: return f"{R}(maj7)"
19066
+ return f"{R}(add{norm_ivs[1]})"
19067
+
19068
+ # Map normalized semitones to standard chord degrees
19069
+ degree_map = {
19070
+ 1: "b9", 2: "9", 3: "m3", 4: "M3", 5: "11", 6: "b5",
19071
+ 7: "P5", 8: "#5", 9: "6", 10: "m7", 11: "M7"
19072
+ }
19073
+
19074
+ degrees_set = set([degree_map[iv] for iv in norm_ivs if iv != 0])
19075
+
19076
+ # Helper variables
19077
+ has_m3 = "m3" in degrees_set
19078
+ has_M3 = "M3" in degrees_set
19079
+ has_P5 = "P5" in degrees_set
19080
+ has_b5 = "b5" in degrees_set
19081
+ has_sharp5 = "#5" in degrees_set
19082
+ has_m7 = "m7" in degrees_set
19083
+ has_M7 = "M7" in degrees_set
19084
+ has_9 = "9" in degrees_set
19085
+ has_b9 = "b9" in degrees_set
19086
+ has_6 = "6" in degrees_set # Corrected: 9 semitones is a 6, not a 13
19087
+ has_11 = "11" in degrees_set
19088
+
19089
+ # 4. Handle Whole-tone clusters (5 or 6 notes of even intervals)
19090
+ # Fixed logic: strictly checks if all intervals are even semitones
19091
+ if len(norm_ivs) >= 5 and all(i % 2 == 0 for i in norm_ivs):
19092
+ return f"{R}(whole-tone cluster)"
19093
+
19094
+ # 3. Determine Base Triad Quality
19095
+ base = ""
19096
+ triad_degrees = []
19097
+
19098
+ if has_M3 and has_P5:
19099
+ base = ""
19100
+ triad_degrees = ["M3", "P5"]
19101
+ elif has_M3 and has_sharp5:
19102
+ base = "aug"
19103
+ triad_degrees = ["M3", "#5"]
19104
+ elif has_M3 and has_b5:
19105
+ base = "(b5)"
19106
+ triad_degrees = ["M3", "b5"]
19107
+ elif has_M3:
19108
+ base = ""
19109
+ triad_degrees = ["M3"]
19110
+ elif has_m3 and has_P5:
19111
+ base = "m"
19112
+ triad_degrees = ["m3", "P5"]
19113
+ elif has_m3 and has_b5:
19114
+ base = "dim"
19115
+ triad_degrees = ["m3", "b5"]
19116
+ elif has_m3 and has_sharp5:
19117
+ base = "m#5"
19118
+ triad_degrees = ["m3", "#5"]
19119
+ elif has_m3:
19120
+ base = "m"
19121
+ triad_degrees = ["m3"]
19122
+ else:
19123
+ # No 3rd present (Ambiguous clusters or sus4/add11 combinations)
19124
+ extensions = []
19125
+ for iv in norm_ivs:
19126
+ if iv == 0: continue
19127
+ if iv == 5: extensions.append("sus4") # 4th implies sus4
19128
+ elif iv == 7: continue # 5th is assumed
19129
+ elif iv == 6: extensions.append("b5")
19130
+ elif iv == 8: extensions.append("#5")
19131
+ elif iv == 2: extensions.append("add9")
19132
+ elif iv == 9: extensions.append("6")
19133
+ elif iv == 10: extensions.append("m7")
19134
+ elif iv == 11: extensions.append("M7")
19135
+ elif iv == 1: extensions.append("addb9")
19136
+ else: extensions.append(f"add{iv}")
19137
+
19138
+ if not extensions: return R
19139
+
19140
+ # Handle sus4 & m7 combo standard naming convention
19141
+ if "sus4" in extensions and "m7" in extensions:
19142
+ extensions.remove("sus4")
19143
+ extensions.remove("m7")
19144
+ return f"{R}7sus4{''.join(extensions)}"
19145
+
19146
+ return f"{R}{''.join(extensions)}"
19147
+
19148
+ # 5. Determine Seventh Chord Quality
19149
+ seventh = ""
19150
+ if has_m7:
19151
+ if base == "":
19152
+ seventh = "7"
19153
+ elif base == "m":
19154
+ if has_b5:
19155
+ seventh = "7b5"
19156
+ triad_degrees.append("b5")
19157
+ else:
19158
+ seventh = "7"
19159
+ elif base == "dim":
19160
+ seventh = "7"
19161
+ elif base == "m#5":
19162
+ seventh = "7"
19163
+ elif base == "(b5)":
19164
+ seventh = "7"
19165
+ else:
19166
+ seventh = "7"
19167
+ elif has_M7:
19168
+ if base == "":
19169
+ seventh = "maj7"
19170
+ elif base == "m":
19171
+ seventh = "maj7"
19172
+ elif base == "dim":
19173
+ seventh = "M7"
19174
+ elif base == "aug":
19175
+ seventh = "maj7"
19176
+ elif base == "m#5":
19177
+ seventh = "maj7"
19178
+ elif base == "(b5)":
19179
+ seventh = "maj7"
19180
+
19181
+ # 6. Identify Extensions and Alterations
19182
+ extensions = []
19183
+ alterations = []
19184
+
19185
+ for iv in norm_ivs:
19186
+ if iv == 0: continue
19187
+ d = degree_map[iv]
19188
+ if d in triad_degrees: continue
19189
+ if d == "m7" and has_m7: continue
19190
+ if d == "M7" and has_M7: continue
19191
+
19192
+ if d == "9":
19193
+ extensions.append(d)
19194
+ elif d == "b9":
19195
+ alterations.append(d)
19196
+ elif d == "11":
19197
+ # Fixed addadd11 bug
19198
+ if has_m7 or has_M7:
19199
+ extensions.append(d)
19200
+ else:
19201
+ extensions.append("add11")
19202
+ elif d == "6":
19203
+ if has_m7 or has_M7:
19204
+ extensions.append(d)
19205
+ else:
19206
+ extensions.append("6")
19207
+ # Prevent conflicting and redundant b5/#5 alteration additions
19208
+ elif d == "P5" and has_b5 and not has_sharp5:
19209
+ alterations.append("#5")
19210
+ elif d == "P5":
19211
+ continue
19212
+ elif d == "#5":
19213
+ if "aug" in base:
19214
+ continue
19215
+ # If both b5 and #5 exist, we don't double-annotate #5 unless in 7th context
19216
+ if has_b5:
19217
+ continue
19218
+ alterations.append(d)
19219
+ elif d == "b5" and "(b5)" in base:
19220
+ continue
19221
+
19222
+ # 7. Formatting Rules
19223
+ # A 6th chord takes priority if no 7th is present
19224
+ is_sixth = "6" in extensions and not seventh
19225
+ if is_sixth:
19226
+ name = f"{R}{base}6"
19227
+ extensions.remove("6")
19228
+ if extensions:
19229
+ name += f"(add{''.join(extensions)})"
19230
+ if alterations:
19231
+ name += f"({','.join(alterations)})"
19232
+ return name
19233
+
19234
+ # Build the core chord name
19235
+ name = f"{R}{base}{seventh}"
19236
+
19237
+ if extensions:
19238
+ name += "".join(extensions)
19239
+
19240
+ if alterations:
19241
+ name += f"({','.join(alterations)})"
19242
+
19243
+ return name
19244
+
19245
+ ###################################################################################
19246
+
19247
  print('Module loaded!')
19248
  print('=' * 70)
19249
  print('Enjoy! :)')