Upload TMIDIX.py
Browse files
TMIDIX.py
CHANGED
|
@@ -51,7 +51,7 @@ r'''############################################################################
|
|
| 51 |
|
| 52 |
###################################################################################
|
| 53 |
|
| 54 |
-
__version__ = "25.
|
| 55 |
|
| 56 |
print('=' * 70)
|
| 57 |
print('TMIDIX Python module')
|
|
@@ -1513,6 +1513,9 @@ import hashlib
|
|
| 1513 |
|
| 1514 |
from array import array
|
| 1515 |
|
|
|
|
|
|
|
|
|
|
| 1516 |
###################################################################################
|
| 1517 |
#
|
| 1518 |
# Original TMIDI Tegridy helper functions
|
|
@@ -3721,19 +3724,52 @@ def validate_pitches(chord, channel_to_check = 0, return_sorted = True):
|
|
| 3721 |
chord.sort(key = lambda x: x[4], reverse=True)
|
| 3722 |
return chord
|
| 3723 |
|
| 3724 |
-
def adjust_score_velocities(score,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3725 |
|
| 3726 |
-
|
| 3727 |
-
|
| 3728 |
-
|
|
|
|
|
|
|
|
|
|
| 3729 |
|
| 3730 |
-
max_channel_velocity = max([c[5] for c in score])
|
| 3731 |
-
if max_channel_velocity < min_velocity:
|
| 3732 |
-
factor = max_velocity / min_velocity
|
| 3733 |
else:
|
| 3734 |
-
|
| 3735 |
-
|
| 3736 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3737 |
|
| 3738 |
def chordify_score(score,
|
| 3739 |
return_choridfied_score=True,
|
|
@@ -5026,54 +5062,102 @@ def patch_list_from_enhanced_score_notes(enhanced_score_notes,
|
|
| 5026 |
|
| 5027 |
###################################################################################
|
| 5028 |
|
| 5029 |
-
def patch_enhanced_score_notes(
|
| 5030 |
-
|
| 5031 |
-
|
| 5032 |
-
|
| 5033 |
-
|
|
|
|
| 5034 |
|
| 5035 |
-
#===========================================================================
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5036 |
|
| 5037 |
enhanced_score_notes_with_patch_changes = []
|
| 5038 |
|
| 5039 |
patches = [-1] * 16
|
| 5040 |
|
|
|
|
|
|
|
|
|
|
| 5041 |
overflow_idx = -1
|
| 5042 |
|
| 5043 |
for idx, e in enumerate(enhanced_score_notes):
|
| 5044 |
-
|
| 5045 |
-
|
| 5046 |
-
|
| 5047 |
-
|
| 5048 |
-
|
| 5049 |
-
|
| 5050 |
-
|
| 5051 |
-
e[3] =
|
| 5052 |
-
|
| 5053 |
-
|
| 5054 |
-
|
| 5055 |
-
|
| 5056 |
-
|
| 5057 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5058 |
|
| 5059 |
-
|
|
|
|
|
|
|
| 5060 |
|
| 5061 |
#===========================================================================
|
| 5062 |
|
| 5063 |
overflow_patches = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5064 |
|
| 5065 |
if overflow_idx != -1:
|
| 5066 |
-
|
| 5067 |
-
|
| 5068 |
-
|
| 5069 |
-
|
| 5070 |
-
|
| 5071 |
-
|
| 5072 |
-
|
| 5073 |
-
|
| 5074 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5075 |
|
| 5076 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5077 |
|
| 5078 |
#===========================================================================
|
| 5079 |
|
|
@@ -5083,9 +5167,13 @@ def patch_enhanced_score_notes(enhanced_score_notes,
|
|
| 5083 |
|
| 5084 |
#===========================================================================
|
| 5085 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5086 |
if verbose:
|
| 5087 |
print('=' * 70)
|
| 5088 |
-
print('
|
| 5089 |
print('=' * 70)
|
| 5090 |
for c, p in enumerate(patches):
|
| 5091 |
print('Cha', str(c).zfill(2), '---', str(p).zfill(3), Number2patch[p])
|
|
@@ -5098,6 +5186,8 @@ def patch_enhanced_score_notes(enhanced_score_notes,
|
|
| 5098 |
print(str(p).zfill(3), Number2patch[p])
|
| 5099 |
print('=' * 70)
|
| 5100 |
|
|
|
|
|
|
|
| 5101 |
return enhanced_score_notes_with_patch_changes, patches, overflow_patches
|
| 5102 |
|
| 5103 |
###################################################################################
|
|
@@ -12961,12 +13051,12 @@ def ordered_groups_unsorted(data, key_index):
|
|
| 12961 |
|
| 12962 |
###################################################################################
|
| 12963 |
|
| 12964 |
-
def ordered_groups(data,
|
| 12965 |
|
| 12966 |
groups = OrderedDict()
|
| 12967 |
|
| 12968 |
for sublist in data:
|
| 12969 |
-
key = sublist[
|
| 12970 |
|
| 12971 |
if key not in groups:
|
| 12972 |
groups[key] = []
|
|
@@ -13110,13 +13200,14 @@ def fix_escore_notes_durations(escore_notes,
|
|
| 13110 |
times_idx=1,
|
| 13111 |
durs_idx=2,
|
| 13112 |
channels_idx = 3,
|
| 13113 |
-
pitches_idx=4
|
|
|
|
| 13114 |
):
|
| 13115 |
|
| 13116 |
notes = [e for e in escore_notes if e[channels_idx] != 9]
|
| 13117 |
drums = [e for e in escore_notes if e[channels_idx] == 9]
|
| 13118 |
|
| 13119 |
-
escore_groups = ordered_groups(notes, pitches_idx)
|
| 13120 |
|
| 13121 |
merged_score = []
|
| 13122 |
|
|
@@ -13131,7 +13222,7 @@ def fix_escore_notes_durations(escore_notes,
|
|
| 13131 |
elif len(g) == 2:
|
| 13132 |
|
| 13133 |
if g[0][times_idx]+g[0][durs_idx] >= g[1][times_idx]:
|
| 13134 |
-
g[0][durs_idx] = max(1, g[1][times_idx] - g[0][times_idx] -
|
| 13135 |
|
| 13136 |
merged_score.extend(g)
|
| 13137 |
|
|
@@ -13142,6 +13233,378 @@ def fix_escore_notes_durations(escore_notes,
|
|
| 13142 |
|
| 13143 |
###################################################################################
|
| 13144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13145 |
print('Module loaded!')
|
| 13146 |
print('=' * 70)
|
| 13147 |
print('Enjoy! :)')
|
|
|
|
| 51 |
|
| 52 |
###################################################################################
|
| 53 |
|
| 54 |
+
__version__ = "25.7.8"
|
| 55 |
|
| 56 |
print('=' * 70)
|
| 57 |
print('TMIDIX Python module')
|
|
|
|
| 1513 |
|
| 1514 |
from array import array
|
| 1515 |
|
| 1516 |
+
from pathlib import Path
|
| 1517 |
+
from fnmatch import fnmatch
|
| 1518 |
+
|
| 1519 |
###################################################################################
|
| 1520 |
#
|
| 1521 |
# Original TMIDI Tegridy helper functions
|
|
|
|
| 3724 |
chord.sort(key = lambda x: x[4], reverse=True)
|
| 3725 |
return chord
|
| 3726 |
|
| 3727 |
+
def adjust_score_velocities(score,
|
| 3728 |
+
max_velocity,
|
| 3729 |
+
adj_per_channel=False,
|
| 3730 |
+
adj_in_place=True
|
| 3731 |
+
):
|
| 3732 |
+
|
| 3733 |
+
if adj_in_place:
|
| 3734 |
+
buf = score
|
| 3735 |
+
|
| 3736 |
+
else:
|
| 3737 |
+
buf = copy.deepcopy(score)
|
| 3738 |
+
|
| 3739 |
+
notes = [evt for evt in buf if evt[0] == 'note']
|
| 3740 |
+
|
| 3741 |
+
if not notes:
|
| 3742 |
+
return buf
|
| 3743 |
+
|
| 3744 |
+
if adj_per_channel:
|
| 3745 |
+
channel_max = {}
|
| 3746 |
+
|
| 3747 |
+
for _, _, _, ch, _, vel, _ in notes:
|
| 3748 |
+
channel_max[ch] = max(channel_max.get(ch, 0), vel)
|
| 3749 |
+
|
| 3750 |
+
channel_factor = {
|
| 3751 |
+
ch: (max_velocity / vmax if vmax > 0 else 1.0)
|
| 3752 |
+
for ch, vmax in channel_max.items()
|
| 3753 |
+
}
|
| 3754 |
|
| 3755 |
+
for evt in buf:
|
| 3756 |
+
if evt[0] == 'note':
|
| 3757 |
+
ch = evt[3]
|
| 3758 |
+
factor = channel_factor.get(ch, 1.0)
|
| 3759 |
+
new_vel = int(evt[5] * factor)
|
| 3760 |
+
evt[5] = max(1, min(127, new_vel))
|
| 3761 |
|
|
|
|
|
|
|
|
|
|
| 3762 |
else:
|
| 3763 |
+
global_max = max(vel for _, _, _, _, _, vel, _ in notes)
|
| 3764 |
+
factor = max_velocity / global_max if global_max > 0 else 1.0
|
| 3765 |
+
|
| 3766 |
+
for evt in buf:
|
| 3767 |
+
if evt[0] == 'note':
|
| 3768 |
+
new_vel = int(evt[5] * factor)
|
| 3769 |
+
evt[5] = max(1, min(127, new_vel))
|
| 3770 |
+
|
| 3771 |
+
if not adj_in_place:
|
| 3772 |
+
return buf
|
| 3773 |
|
| 3774 |
def chordify_score(score,
|
| 3775 |
return_choridfied_score=True,
|
|
|
|
| 5062 |
|
| 5063 |
###################################################################################
|
| 5064 |
|
| 5065 |
+
def patch_enhanced_score_notes(escore_notes,
|
| 5066 |
+
reserved_patch=-1,
|
| 5067 |
+
reserved_patch_channel=-1,
|
| 5068 |
+
drums_patch=9,
|
| 5069 |
+
verbose=False
|
| 5070 |
+
):
|
| 5071 |
|
| 5072 |
+
#===========================================================================
|
| 5073 |
+
|
| 5074 |
+
enhanced_score_notes = copy.deepcopy(escore_notes)
|
| 5075 |
+
|
| 5076 |
+
#===========================================================================
|
| 5077 |
|
| 5078 |
enhanced_score_notes_with_patch_changes = []
|
| 5079 |
|
| 5080 |
patches = [-1] * 16
|
| 5081 |
|
| 5082 |
+
if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
|
| 5083 |
+
patches[reserved_patch_channel] = reserved_patch
|
| 5084 |
+
|
| 5085 |
overflow_idx = -1
|
| 5086 |
|
| 5087 |
for idx, e in enumerate(enhanced_score_notes):
|
| 5088 |
+
if e[0] == 'note':
|
| 5089 |
+
if e[3] != 9:
|
| 5090 |
+
if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
|
| 5091 |
+
if e[6] == reserved_patch:
|
| 5092 |
+
e[3] = reserved_patch_channel
|
| 5093 |
+
|
| 5094 |
+
if patches[e[3]] == -1:
|
| 5095 |
+
patches[e[3]] = e[6]
|
| 5096 |
+
|
| 5097 |
+
else:
|
| 5098 |
+
if patches[e[3]] != e[6]:
|
| 5099 |
+
if e[6] in patches:
|
| 5100 |
+
e[3] = patches.index(e[6])
|
| 5101 |
+
|
| 5102 |
+
else:
|
| 5103 |
+
if -1 in patches:
|
| 5104 |
+
patches[patches.index(-1)] = e[6]
|
| 5105 |
+
|
| 5106 |
+
else:
|
| 5107 |
+
overflow_idx = idx
|
| 5108 |
+
break
|
| 5109 |
|
| 5110 |
+
enhanced_score_notes_with_patch_changes.append(e)
|
| 5111 |
+
|
| 5112 |
+
print(patches)
|
| 5113 |
|
| 5114 |
#===========================================================================
|
| 5115 |
|
| 5116 |
overflow_patches = []
|
| 5117 |
+
overflow_channels = [-1] * 16
|
| 5118 |
+
overflow_channels[9] = drums_patch
|
| 5119 |
+
|
| 5120 |
+
if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
|
| 5121 |
+
overflow_channels[reserved_patch_channel] = reserved_patch
|
| 5122 |
|
| 5123 |
if overflow_idx != -1:
|
| 5124 |
+
for idx, e in enumerate(enhanced_score_notes[overflow_idx:]):
|
| 5125 |
+
if e[0] == 'note':
|
| 5126 |
+
if e[3] != 9:
|
| 5127 |
+
if e[6] not in overflow_channels:
|
| 5128 |
+
|
| 5129 |
+
if -1 in overflow_channels:
|
| 5130 |
+
free_chan = overflow_channels.index(-1)
|
| 5131 |
+
overflow_channels[free_chan] = e[6]
|
| 5132 |
+
e[3] = free_chan
|
| 5133 |
+
|
| 5134 |
+
enhanced_score_notes_with_patch_changes.append(['patch_change', e[1], e[3], e[6]])
|
| 5135 |
+
|
| 5136 |
+
overflow_patches.append(e[6])
|
| 5137 |
+
|
| 5138 |
+
else:
|
| 5139 |
+
overflow_channels = [-1] * 16
|
| 5140 |
+
overflow_channels[9] = drums_patch
|
| 5141 |
+
|
| 5142 |
+
if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
|
| 5143 |
+
overflow_channels[reserved_patch_channel] = reserved_patch
|
| 5144 |
+
e[3] = reserved_patch_channel
|
| 5145 |
+
|
| 5146 |
+
if e[6] != reserved_patch:
|
| 5147 |
+
|
| 5148 |
+
free_chan = overflow_channels.index(-1)
|
| 5149 |
+
e[3] = free_chan
|
| 5150 |
+
|
| 5151 |
+
overflow_channels[e[3]] = e[6]
|
| 5152 |
|
| 5153 |
+
enhanced_score_notes_with_patch_changes.append(['patch_change', e[1], e[3], e[6]])
|
| 5154 |
+
|
| 5155 |
+
overflow_patches.append(e[6])
|
| 5156 |
+
|
| 5157 |
+
else:
|
| 5158 |
+
e[3] = overflow_channels.index(e[6])
|
| 5159 |
+
|
| 5160 |
+
enhanced_score_notes_with_patch_changes.append(e)
|
| 5161 |
|
| 5162 |
#===========================================================================
|
| 5163 |
|
|
|
|
| 5167 |
|
| 5168 |
#===========================================================================
|
| 5169 |
|
| 5170 |
+
overflow_patches = ordered_set(overflow_patches)
|
| 5171 |
+
|
| 5172 |
+
#===========================================================================
|
| 5173 |
+
|
| 5174 |
if verbose:
|
| 5175 |
print('=' * 70)
|
| 5176 |
+
print('Main composition patches')
|
| 5177 |
print('=' * 70)
|
| 5178 |
for c, p in enumerate(patches):
|
| 5179 |
print('Cha', str(c).zfill(2), '---', str(p).zfill(3), Number2patch[p])
|
|
|
|
| 5186 |
print(str(p).zfill(3), Number2patch[p])
|
| 5187 |
print('=' * 70)
|
| 5188 |
|
| 5189 |
+
#===========================================================================
|
| 5190 |
+
|
| 5191 |
return enhanced_score_notes_with_patch_changes, patches, overflow_patches
|
| 5192 |
|
| 5193 |
###################################################################################
|
|
|
|
| 13051 |
|
| 13052 |
###################################################################################
|
| 13053 |
|
| 13054 |
+
def ordered_groups(data, ptc_idx, pat_idx):
|
| 13055 |
|
| 13056 |
groups = OrderedDict()
|
| 13057 |
|
| 13058 |
for sublist in data:
|
| 13059 |
+
key = tuple([sublist[ptc_idx], sublist[pat_idx]])
|
| 13060 |
|
| 13061 |
if key not in groups:
|
| 13062 |
groups[key] = []
|
|
|
|
| 13200 |
times_idx=1,
|
| 13201 |
durs_idx=2,
|
| 13202 |
channels_idx = 3,
|
| 13203 |
+
pitches_idx=4,
|
| 13204 |
+
patches_idx=6
|
| 13205 |
):
|
| 13206 |
|
| 13207 |
notes = [e for e in escore_notes if e[channels_idx] != 9]
|
| 13208 |
drums = [e for e in escore_notes if e[channels_idx] == 9]
|
| 13209 |
|
| 13210 |
+
escore_groups = ordered_groups(notes, pitches_idx, patches_idx)
|
| 13211 |
|
| 13212 |
merged_score = []
|
| 13213 |
|
|
|
|
| 13222 |
elif len(g) == 2:
|
| 13223 |
|
| 13224 |
if g[0][times_idx]+g[0][durs_idx] >= g[1][times_idx]:
|
| 13225 |
+
g[0][durs_idx] = max(1, g[1][times_idx] - g[0][times_idx] - min_notes_gap)
|
| 13226 |
|
| 13227 |
merged_score.extend(g)
|
| 13228 |
|
|
|
|
| 13233 |
|
| 13234 |
###################################################################################
|
| 13235 |
|
| 13236 |
+
def create_nested_chords_tree(chords_list):
|
| 13237 |
+
|
| 13238 |
+
tree = {}
|
| 13239 |
+
|
| 13240 |
+
for chord in chords_list:
|
| 13241 |
+
|
| 13242 |
+
node = tree
|
| 13243 |
+
|
| 13244 |
+
for semitone in chord:
|
| 13245 |
+
if semitone not in node:
|
| 13246 |
+
node[semitone] = {}
|
| 13247 |
+
|
| 13248 |
+
node = node[semitone]
|
| 13249 |
+
|
| 13250 |
+
node.setdefault(-1, []).append(chord)
|
| 13251 |
+
|
| 13252 |
+
return tree
|
| 13253 |
+
|
| 13254 |
+
###################################################################################
|
| 13255 |
+
|
| 13256 |
+
def get_chords_with_prefix(nested_chords_tree, prefix):
|
| 13257 |
+
|
| 13258 |
+
node = nested_chords_tree
|
| 13259 |
+
|
| 13260 |
+
for semitone in prefix:
|
| 13261 |
+
if semitone in node:
|
| 13262 |
+
node = node[semitone]
|
| 13263 |
+
|
| 13264 |
+
else:
|
| 13265 |
+
return []
|
| 13266 |
+
|
| 13267 |
+
collected_chords = []
|
| 13268 |
+
|
| 13269 |
+
def recursive_collect(subnode):
|
| 13270 |
+
if -1 in subnode:
|
| 13271 |
+
collected_chords.extend(subnode[-1])
|
| 13272 |
+
|
| 13273 |
+
for key, child in subnode.items():
|
| 13274 |
+
if key != -1:
|
| 13275 |
+
recursive_collect(child)
|
| 13276 |
+
|
| 13277 |
+
recursive_collect(node)
|
| 13278 |
+
|
| 13279 |
+
return collected_chords
|
| 13280 |
+
|
| 13281 |
+
###################################################################################
|
| 13282 |
+
|
| 13283 |
+
def get_chords_by_semitones(chords_list, chord_semitones):
|
| 13284 |
+
|
| 13285 |
+
query_set = set(chord_semitones)
|
| 13286 |
+
results = []
|
| 13287 |
+
|
| 13288 |
+
for chord in chords_list:
|
| 13289 |
+
|
| 13290 |
+
chord_set = set(chord)
|
| 13291 |
+
|
| 13292 |
+
if query_set.issubset(chord_set):
|
| 13293 |
+
results.append(sorted(set(chord)))
|
| 13294 |
+
|
| 13295 |
+
return results
|
| 13296 |
+
|
| 13297 |
+
###################################################################################
|
| 13298 |
+
|
| 13299 |
+
def remove_duplicate_pitches_from_escore_notes(escore_notes,
|
| 13300 |
+
pitches_idx=4,
|
| 13301 |
+
patches_idx=6,
|
| 13302 |
+
return_dupes_count=False
|
| 13303 |
+
):
|
| 13304 |
+
|
| 13305 |
+
cscore = chordify_score([1000, escore_notes])
|
| 13306 |
+
|
| 13307 |
+
new_escore = []
|
| 13308 |
+
|
| 13309 |
+
bp_count = 0
|
| 13310 |
+
|
| 13311 |
+
for c in cscore:
|
| 13312 |
+
|
| 13313 |
+
cho = []
|
| 13314 |
+
seen = []
|
| 13315 |
+
|
| 13316 |
+
for cc in c:
|
| 13317 |
+
if [cc[pitches_idx], cc[patches_idx]] not in seen:
|
| 13318 |
+
cho.append(cc)
|
| 13319 |
+
seen.append([cc[pitches_idx], cc[patches_idx]])
|
| 13320 |
+
|
| 13321 |
+
else:
|
| 13322 |
+
bp_count += 1
|
| 13323 |
+
|
| 13324 |
+
new_escore.extend(cho)
|
| 13325 |
+
|
| 13326 |
+
if return_dupes_count:
|
| 13327 |
+
return bp_count
|
| 13328 |
+
|
| 13329 |
+
else:
|
| 13330 |
+
return new_escore
|
| 13331 |
+
|
| 13332 |
+
###################################################################################
|
| 13333 |
+
|
| 13334 |
+
def chunks_shuffle(lst,
|
| 13335 |
+
min_len=1,
|
| 13336 |
+
max_len=3,
|
| 13337 |
+
seed=None
|
| 13338 |
+
):
|
| 13339 |
+
|
| 13340 |
+
rnd = random.Random(seed)
|
| 13341 |
+
chunks = []
|
| 13342 |
+
i, n = 0, len(lst)
|
| 13343 |
+
|
| 13344 |
+
while i < n:
|
| 13345 |
+
size = rnd.randint(min_len, max_len)
|
| 13346 |
+
size = min(size, n - i)
|
| 13347 |
+
chunks.append(lst[i : i + size])
|
| 13348 |
+
i += size
|
| 13349 |
+
|
| 13350 |
+
rnd.shuffle(chunks)
|
| 13351 |
+
|
| 13352 |
+
flattened = []
|
| 13353 |
+
for chunk in chunks:
|
| 13354 |
+
flattened.extend(chunk)
|
| 13355 |
+
|
| 13356 |
+
return flattened
|
| 13357 |
+
|
| 13358 |
+
###################################################################################
|
| 13359 |
+
|
| 13360 |
+
def convert_bytes_in_nested_list(lst,
|
| 13361 |
+
encoding='utf-8',
|
| 13362 |
+
errors='ignore',
|
| 13363 |
+
return_changed_events_count=False
|
| 13364 |
+
):
|
| 13365 |
+
|
| 13366 |
+
new_list = []
|
| 13367 |
+
|
| 13368 |
+
ce_count = 0
|
| 13369 |
+
|
| 13370 |
+
for item in lst:
|
| 13371 |
+
if isinstance(item, list):
|
| 13372 |
+
new_list.append(convert_bytes_in_nested_list(item))
|
| 13373 |
+
|
| 13374 |
+
elif isinstance(item, bytes):
|
| 13375 |
+
new_list.append(item.decode(encoding, errors=errors))
|
| 13376 |
+
ce_count += 1
|
| 13377 |
+
|
| 13378 |
+
else:
|
| 13379 |
+
new_list.append(item)
|
| 13380 |
+
|
| 13381 |
+
if return_changed_events_count:
|
| 13382 |
+
return new_list, ce_count
|
| 13383 |
+
|
| 13384 |
+
else:
|
| 13385 |
+
return new_list
|
| 13386 |
+
|
| 13387 |
+
###################################################################################
|
| 13388 |
+
|
| 13389 |
+
def find_deepest_midi_dirs(roots,
|
| 13390 |
+
marker_file="midi_score.mid",
|
| 13391 |
+
suffixes=None,
|
| 13392 |
+
randomize=False,
|
| 13393 |
+
seed=None,
|
| 13394 |
+
verbose=False
|
| 13395 |
+
):
|
| 13396 |
+
|
| 13397 |
+
try:
|
| 13398 |
+
iter(roots)
|
| 13399 |
+
if isinstance(roots, (str, Path)):
|
| 13400 |
+
root_list = [roots]
|
| 13401 |
+
else:
|
| 13402 |
+
root_list = list(roots)
|
| 13403 |
+
|
| 13404 |
+
except TypeError:
|
| 13405 |
+
root_list = [roots]
|
| 13406 |
+
|
| 13407 |
+
if isinstance(marker_file, (list, tuple)):
|
| 13408 |
+
patterns = [p.lower() for p in marker_file if p]
|
| 13409 |
+
|
| 13410 |
+
else:
|
| 13411 |
+
patterns = [marker_file.lower()] if marker_file else []
|
| 13412 |
+
|
| 13413 |
+
allowed = {s.lower() for s in (suffixes or ['.mid', '.midi', '.kar'])}
|
| 13414 |
+
|
| 13415 |
+
if verbose:
|
| 13416 |
+
print("Settings:")
|
| 13417 |
+
print(" Roots:", [str(r) for r in root_list])
|
| 13418 |
+
print(" Marker patterns:", patterns or "<no marker filter>")
|
| 13419 |
+
print(" Allowed suffixes:", allowed)
|
| 13420 |
+
print(f" Randomize={randomize}, Seed={seed}")
|
| 13421 |
+
|
| 13422 |
+
results = defaultdict(list)
|
| 13423 |
+
rng = random.Random(seed)
|
| 13424 |
+
|
| 13425 |
+
for root in root_list:
|
| 13426 |
+
|
| 13427 |
+
root_path = Path(root)
|
| 13428 |
+
|
| 13429 |
+
if not root_path.is_dir():
|
| 13430 |
+
print(f"Warning: '{root_path}' is not a valid directory, skipping.")
|
| 13431 |
+
continue
|
| 13432 |
+
|
| 13433 |
+
if verbose:
|
| 13434 |
+
print(f"\nScanning root: {str(root_path)}")
|
| 13435 |
+
|
| 13436 |
+
all_dirs = list(root_path.rglob("*"))
|
| 13437 |
+
dirs_iter = tqdm.tqdm(all_dirs, desc=f"Dirs in {root_path.name}", disable=not verbose)
|
| 13438 |
+
|
| 13439 |
+
for dirpath in dirs_iter:
|
| 13440 |
+
if not dirpath.is_dir():
|
| 13441 |
+
continue
|
| 13442 |
+
|
| 13443 |
+
children = list(dirpath.iterdir())
|
| 13444 |
+
if any(child.is_dir() for child in children):
|
| 13445 |
+
if verbose:
|
| 13446 |
+
print(f"Skipping non-leaf: {str(dirpath)}")
|
| 13447 |
+
continue
|
| 13448 |
+
|
| 13449 |
+
files = [f for f in children if f.is_file()]
|
| 13450 |
+
names = [f.name.lower() for f in files]
|
| 13451 |
+
|
| 13452 |
+
if patterns:
|
| 13453 |
+
matched = any(fnmatch(name, pat) for name in names for pat in patterns)
|
| 13454 |
+
if not matched:
|
| 13455 |
+
if verbose:
|
| 13456 |
+
print(f"No marker in: {str(dirpath)}")
|
| 13457 |
+
continue
|
| 13458 |
+
|
| 13459 |
+
if verbose:
|
| 13460 |
+
print(f"Marker found in: {str(dirpath)}")
|
| 13461 |
+
|
| 13462 |
+
else:
|
| 13463 |
+
if verbose:
|
| 13464 |
+
print(f"Including leaf (no marker): {str(dirpath)}")
|
| 13465 |
+
|
| 13466 |
+
for f in files:
|
| 13467 |
+
if f.suffix.lower() in allowed:
|
| 13468 |
+
results[str(dirpath)].append(str(f))
|
| 13469 |
+
|
| 13470 |
+
if verbose:
|
| 13471 |
+
print(f" Collected: {f.name}")
|
| 13472 |
+
|
| 13473 |
+
all_leaves = list(results.keys())
|
| 13474 |
+
if randomize:
|
| 13475 |
+
if verbose:
|
| 13476 |
+
print("\nShuffling leaf directories")
|
| 13477 |
+
|
| 13478 |
+
rng.shuffle(all_leaves)
|
| 13479 |
+
|
| 13480 |
+
else:
|
| 13481 |
+
all_leaves.sort()
|
| 13482 |
+
|
| 13483 |
+
final_dict = {}
|
| 13484 |
+
|
| 13485 |
+
for leaf in all_leaves:
|
| 13486 |
+
file_list = results[leaf][:]
|
| 13487 |
+
if randomize:
|
| 13488 |
+
if verbose:
|
| 13489 |
+
print(f"Shuffling files in: {leaf}")
|
| 13490 |
+
|
| 13491 |
+
rng.shuffle(file_list)
|
| 13492 |
+
|
| 13493 |
+
else:
|
| 13494 |
+
file_list.sort()
|
| 13495 |
+
|
| 13496 |
+
final_dict[leaf] = file_list
|
| 13497 |
+
|
| 13498 |
+
if verbose:
|
| 13499 |
+
print("\nScan complete. Found directories:")
|
| 13500 |
+
for d, fl in final_dict.items():
|
| 13501 |
+
print(f" {d} -> {len(fl)} files")
|
| 13502 |
+
|
| 13503 |
+
return final_dict
|
| 13504 |
+
|
| 13505 |
+
###################################################################################
|
| 13506 |
+
|
| 13507 |
+
PERCUSSION_GROUPS = {
|
| 13508 |
+
|
| 13509 |
+
1: { # Bass Drums
|
| 13510 |
+
35: 'Acoustic Bass Drum',
|
| 13511 |
+
36: 'Bass Drum 1',
|
| 13512 |
+
},
|
| 13513 |
+
2: { # Stick
|
| 13514 |
+
37: 'Side Stick',
|
| 13515 |
+
},
|
| 13516 |
+
3: { # Snares
|
| 13517 |
+
38: 'Acoustic Snare',
|
| 13518 |
+
40: 'Electric Snare',
|
| 13519 |
+
},
|
| 13520 |
+
4: { # Claps
|
| 13521 |
+
39: 'Hand Clap',
|
| 13522 |
+
},
|
| 13523 |
+
5: { # Floor Toms
|
| 13524 |
+
41: 'Low Floor Tom',
|
| 13525 |
+
43: 'High Floor Tom',
|
| 13526 |
+
},
|
| 13527 |
+
6: { # Hi-Hats
|
| 13528 |
+
42: 'Closed Hi-Hat',
|
| 13529 |
+
44: 'Pedal Hi-Hat',
|
| 13530 |
+
46: 'Open Hi-Hat',
|
| 13531 |
+
},
|
| 13532 |
+
7: { # Toms
|
| 13533 |
+
45: 'Low Tom',
|
| 13534 |
+
47: 'Low-Mid Tom',
|
| 13535 |
+
48: 'Hi-Mid Tom',
|
| 13536 |
+
50: 'High Tom',
|
| 13537 |
+
},
|
| 13538 |
+
8: { # Cymbals
|
| 13539 |
+
49: 'Crash Cymbal 1',
|
| 13540 |
+
51: 'Ride Cymbal 1',
|
| 13541 |
+
52: 'Chinese Cymbal',
|
| 13542 |
+
55: 'Splash Cymbal',
|
| 13543 |
+
57: 'Crash Cymbal 2',
|
| 13544 |
+
59: 'Ride Cymbal 2',
|
| 13545 |
+
},
|
| 13546 |
+
9: { # Bells
|
| 13547 |
+
53: 'Ride Bell',
|
| 13548 |
+
},
|
| 13549 |
+
10: { # Tambourine
|
| 13550 |
+
54: 'Tambourine',
|
| 13551 |
+
},
|
| 13552 |
+
11: { # Cowbell
|
| 13553 |
+
56: 'Cowbell',
|
| 13554 |
+
},
|
| 13555 |
+
12: { # Vibraslap
|
| 13556 |
+
58: 'Vibraslap',
|
| 13557 |
+
},
|
| 13558 |
+
13: { # Bongos
|
| 13559 |
+
60: 'Hi Bongo',
|
| 13560 |
+
61: 'Low Bongo',
|
| 13561 |
+
},
|
| 13562 |
+
14: { # Congas
|
| 13563 |
+
62: 'Mute Hi Conga',
|
| 13564 |
+
63: 'Open Hi Conga',
|
| 13565 |
+
64: 'Low Conga',
|
| 13566 |
+
},
|
| 13567 |
+
15: { # Timbales
|
| 13568 |
+
65: 'High Timbale',
|
| 13569 |
+
66: 'Low Timbale',
|
| 13570 |
+
},
|
| 13571 |
+
16: { # Agogô
|
| 13572 |
+
67: 'High Agogo',
|
| 13573 |
+
68: 'Low Agogo',
|
| 13574 |
+
},
|
| 13575 |
+
17: { # Cabasa
|
| 13576 |
+
69: 'Cabasa',
|
| 13577 |
+
},
|
| 13578 |
+
18: { # Maracas
|
| 13579 |
+
70: 'Maracas',
|
| 13580 |
+
},
|
| 13581 |
+
19: { # Whistles
|
| 13582 |
+
71: 'Short Whistle',
|
| 13583 |
+
72: 'Long Whistle',
|
| 13584 |
+
},
|
| 13585 |
+
20: { # Guiros
|
| 13586 |
+
73: 'Short Guiro',
|
| 13587 |
+
74: 'Long Guiro',
|
| 13588 |
+
},
|
| 13589 |
+
21: { # Claves
|
| 13590 |
+
75: 'Claves',
|
| 13591 |
+
},
|
| 13592 |
+
22: { # Wood Blocks
|
| 13593 |
+
76: 'Hi Wood Block',
|
| 13594 |
+
77: 'Low Wood Block',
|
| 13595 |
+
},
|
| 13596 |
+
23: { # Cuica
|
| 13597 |
+
78: 'Mute Cuica',
|
| 13598 |
+
79: 'Open Cuica',
|
| 13599 |
+
},
|
| 13600 |
+
24: { # Triangles
|
| 13601 |
+
80: 'Mute Triangle',
|
| 13602 |
+
81: 'Open Triangle',
|
| 13603 |
+
},
|
| 13604 |
+
}
|
| 13605 |
+
|
| 13606 |
+
###################################################################################
|
| 13607 |
+
|
| 13608 |
print('Module loaded!')
|
| 13609 |
print('=' * 70)
|
| 13610 |
print('Enjoy! :)')
|