asigalov61 commited on
Commit
5e69c91
verified
1 Parent(s): a1f80d9

Upload TMIDIX.py

Browse files
Files changed (1) hide show
  1. TMIDIX.py +933 -77
TMIDIX.py CHANGED
@@ -5,9 +5,8 @@ r'''############################################################################
5
  #
6
  #
7
  # Tegridy MIDI X Module (TMIDI X / tee-midi eks)
8
- # Version 1.0
9
  #
10
- # NOTE: TMIDI X Module starts after the partial MIDI.py module @ line 1438
11
  #
12
  # Based upon MIDI.py module v.6.7. by Peter Billam / pjb.com.au
13
  #
@@ -26,7 +25,7 @@ r'''############################################################################
26
  # you may not use this file except in compliance with the License.
27
  # You may obtain a copy of the License at
28
  #
29
- # http://www.apache.org/licenses/LICENSE-2.0
30
  #
31
  # Unless required by applicable law or agreed to in writing, software
32
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -47,7 +46,20 @@ r'''############################################################################
47
  # Copyright 2020 Peter Billam
48
  #
49
  ###################################################################################
50
- ###################################################################################'''
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  import sys, struct, copy
53
 
@@ -1440,7 +1452,6 @@ def _encode(events_lol, unknown_callback=None, never_add_eot=False,
1440
  ###################################################################################
1441
  #
1442
  # Tegridy MIDI X Module (TMIDI X / tee-midi eks)
1443
- # Version 1.0
1444
  #
1445
  # Based upon and includes the amazing MIDI.py module v.6.7. by Peter Billam
1446
  # pjb.com.au
@@ -1477,6 +1488,7 @@ from itertools import groupby
1477
 
1478
  from collections import Counter
1479
  from collections import defaultdict
 
1480
 
1481
  from operator import itemgetter
1482
 
@@ -1497,6 +1509,13 @@ from pathlib import Path
1497
 
1498
  import shutil
1499
 
 
 
 
 
 
 
 
1500
  ###################################################################################
1501
  #
1502
  # Original TMIDI Tegridy helper functions
@@ -3705,19 +3724,52 @@ def validate_pitches(chord, channel_to_check = 0, return_sorted = True):
3705
  chord.sort(key = lambda x: x[4], reverse=True)
3706
  return chord
3707
 
3708
- def adjust_score_velocities(score, max_velocity):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3709
 
3710
- min_velocity = min([c[5] for c in score])
3711
- max_velocity_all_channels = max([c[5] for c in score])
3712
- min_velocity_ratio = min_velocity / max_velocity_all_channels
 
 
 
 
 
 
 
 
3713
 
3714
- max_channel_velocity = max([c[5] for c in score])
3715
- if max_channel_velocity < min_velocity:
3716
- factor = max_velocity / min_velocity
3717
  else:
3718
- factor = max_velocity / max_channel_velocity
3719
- for i in range(len(score)):
3720
- score[i][5] = int(score[i][5] * factor)
 
 
 
 
 
 
 
3721
 
3722
  def chordify_score(score,
3723
  return_choridfied_score=True,
@@ -3849,7 +3901,10 @@ def chordify_score(score,
3849
  else:
3850
  return None
3851
 
3852
- def fix_monophonic_score_durations(monophonic_score):
 
 
 
3853
 
3854
  fixed_score = []
3855
 
@@ -3861,15 +3916,17 @@ def fix_monophonic_score_durations(monophonic_score):
3861
  nmt = monophonic_score[i+1][1]
3862
 
3863
  if note[1]+note[2] >= nmt:
3864
- note_dur = nmt-note[1]-1
3865
  else:
3866
  note_dur = note[2]
3867
 
3868
  new_note = [note[0], note[1], note_dur] + note[3:]
3869
-
3870
- fixed_score.append(new_note)
3871
-
3872
- fixed_score.append(monophonic_score[-1])
 
 
3873
 
3874
  elif type(monophonic_score[0][0]) == int:
3875
 
@@ -3879,15 +3936,17 @@ def fix_monophonic_score_durations(monophonic_score):
3879
  nmt = monophonic_score[i+1][0]
3880
 
3881
  if note[0]+note[1] >= nmt:
3882
- note_dur = nmt-note[0]-1
3883
  else:
3884
  note_dur = note[1]
3885
-
3886
  new_note = [note[0], note_dur] + note[2:]
3887
-
3888
- fixed_score.append(new_note)
3889
-
3890
- fixed_score.append(monophonic_score[-1])
 
 
3891
 
3892
  return fixed_score
3893
 
@@ -5003,54 +5062,101 @@ def patch_list_from_enhanced_score_notes(enhanced_score_notes,
5003
 
5004
  ###################################################################################
5005
 
5006
- def patch_enhanced_score_notes(enhanced_score_notes,
5007
- default_patch=0,
5008
- drums_patch=9,
5009
- verbose=False
5010
- ):
 
 
5011
 
5012
- #===========================================================================
 
 
 
 
5013
 
5014
  enhanced_score_notes_with_patch_changes = []
5015
 
5016
  patches = [-1] * 16
5017
 
 
 
 
5018
  overflow_idx = -1
5019
 
5020
  for idx, e in enumerate(enhanced_score_notes):
5021
- if e[0] == 'note':
5022
- if e[3] != 9:
5023
- if patches[e[3]] == -1:
5024
- patches[e[3]] = e[6]
5025
- else:
5026
- if patches[e[3]] != e[6]:
5027
- if e[6] in patches:
5028
- e[3] = patches.index(e[6])
5029
- else:
5030
- if -1 in patches:
5031
- patches[patches.index(-1)] = e[6]
5032
- else:
5033
- overflow_idx = idx
5034
- break
5035
-
5036
- enhanced_score_notes_with_patch_changes.append(e)
 
 
 
 
 
 
 
5037
 
5038
  #===========================================================================
5039
 
5040
  overflow_patches = []
 
 
 
 
 
5041
 
5042
  if overflow_idx != -1:
5043
- for idx, e in enumerate(enhanced_score_notes[overflow_idx:]):
5044
- if e[0] == 'note':
5045
- if e[3] != 9:
5046
- if e[6] not in patches:
5047
- if e[6] not in overflow_patches:
5048
- overflow_patches.append(e[6])
5049
- enhanced_score_notes_with_patch_changes.append(['patch_change', e[1], e[3], e[6]])
5050
- else:
5051
- e[3] = patches.index(e[6])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5052
 
5053
- enhanced_score_notes_with_patch_changes.append(e)
5054
 
5055
  #===========================================================================
5056
 
@@ -5060,9 +5166,13 @@ def patch_enhanced_score_notes(enhanced_score_notes,
5060
 
5061
  #===========================================================================
5062
 
 
 
 
 
5063
  if verbose:
5064
  print('=' * 70)
5065
- print('Composition patches')
5066
  print('=' * 70)
5067
  for c, p in enumerate(patches):
5068
  print('Cha', str(c).zfill(2), '---', str(p).zfill(3), Number2patch[p])
@@ -5075,6 +5185,8 @@ def patch_enhanced_score_notes(enhanced_score_notes,
5075
  print(str(p).zfill(3), Number2patch[p])
5076
  print('=' * 70)
5077
 
 
 
5078
  return enhanced_score_notes_with_patch_changes, patches, overflow_patches
5079
 
5080
  ###################################################################################
@@ -11170,13 +11282,17 @@ def escore_notes_core(escore_notes, core_len=128):
11170
 
11171
  ###################################################################################
11172
 
11173
- def multiprocessing_wrapper(function, data_list):
11174
 
11175
  with multiprocessing.Pool() as pool:
11176
 
11177
  results = []
11178
 
11179
- for result in tqdm.tqdm(pool.imap_unordered(function, data_list), total=len(data_list)):
 
 
 
 
11180
  results.append(result)
11181
 
11182
  return results
@@ -11325,28 +11441,83 @@ def system_memory_utilization(return_dict=False):
11325
 
11326
  ###################################################################################
11327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11328
  def create_files_list(datasets_paths=['./'],
11329
  files_exts=['.mid', '.midi', '.kar', '.MID', '.MIDI', '.KAR'],
 
 
 
11330
  randomize_files_list=True,
 
 
 
11331
  verbose=True
11332
  ):
 
11333
  if verbose:
11334
  print('=' * 70)
11335
  print('Searching for files...')
11336
  print('This may take a while on a large dataset in particular...')
11337
  print('=' * 70)
11338
 
11339
- filez_set = defaultdict(None)
11340
-
11341
  files_exts = tuple(files_exts)
11342
 
11343
- for dataset_addr in tqdm.tqdm(datasets_paths, disable=not verbose):
11344
- for dirpath, dirnames, filenames in os.walk(dataset_addr):
11345
- for file in filenames:
11346
- if file not in filez_set and file.endswith(files_exts):
11347
- filez_set[os.path.join(dirpath, file)] = None
11348
-
11349
- filez = list(filez_set.keys())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11350
 
11351
  if verbose:
11352
  print('Done!')
@@ -11366,6 +11537,7 @@ def create_files_list(datasets_paths=['./'],
11366
 
11367
  if verbose:
11368
  print('Found', len(filez), 'files.')
 
11369
  print('=' * 70)
11370
 
11371
  else:
@@ -11373,8 +11545,20 @@ def create_files_list(datasets_paths=['./'],
11373
  print('Could not find any files...')
11374
  print('Please check dataset dirs and files extensions...')
11375
  print('=' * 70)
 
 
 
 
11376
 
11377
- return filez
 
 
 
 
 
 
 
 
11378
 
11379
  ###################################################################################
11380
 
@@ -12196,6 +12380,12 @@ def escore_notes_pitches_chords_signature(escore_notes,
12196
  use_full_chords=False
12197
  ):
12198
 
 
 
 
 
 
 
12199
  max_patch = max(0, min(128, max_patch))
12200
 
12201
  escore_notes = [e for e in escore_notes if e[6] <= max_patch]
@@ -12207,7 +12397,7 @@ def escore_notes_pitches_chords_signature(escore_notes,
12207
  sig = []
12208
  dsig = []
12209
 
12210
- drums_offset = 321 + 128
12211
 
12212
  bad_chords_counter = 0
12213
 
@@ -12224,10 +12414,10 @@ def escore_notes_pitches_chords_signature(escore_notes,
12224
  tones_chord = sorted(set([p % 12 for p in pitches]))
12225
 
12226
  try:
12227
- sig_token = ALL_CHORDS_SORTED.index(tones_chord) + 128
12228
  except:
12229
  checked_tones_chord = check_and_fix_tones_chord(tones_chord, use_full_chords=use_full_chords)
12230
- sig_token = ALL_CHORDS_SORTED.index(checked_tones_chord) + 128
12231
  bad_chords_counter += 1
12232
 
12233
  elif len(pitches) == 1:
@@ -12438,10 +12628,10 @@ def merge_chords(chord1, chord2, sort_drums_last=False):
12438
  e[1] = mchord[0][1]
12439
 
12440
  if sort_drums_last:
12441
- mchord.sort(key=lambda x: (x[4], x[6]) if x[6] != 128 else (x[6], -x[4]))
12442
 
12443
  else:
12444
- mchord.sort(key=lambda x: (x[4], x[6]))
12445
 
12446
  return mchord
12447
 
@@ -12753,6 +12943,672 @@ def multi_instrumental_escore_notes_tokenized(escore_notes, compress_seq=False):
12753
 
12754
  return melody_chords
12755
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12756
  ###################################################################################
12757
  # This is the end of the TMIDI X Python module
12758
  ###################################################################################
 
5
  #
6
  #
7
  # Tegridy MIDI X Module (TMIDI X / tee-midi eks)
 
8
  #
9
+ # NOTE: TMIDI X Module starts after the partial MIDI.py module @ line 1450
10
  #
11
  # Based upon MIDI.py module v.6.7. by Peter Billam / pjb.com.au
12
  #
 
25
  # you may not use this file except in compliance with the License.
26
  # You may obtain a copy of the License at
27
  #
28
+ # http://www.apache.org/licenses/LICENSE-2.0
29
  #
30
  # Unless required by applicable law or agreed to in writing, software
31
  # distributed under the License is distributed on an "AS IS" BASIS,
 
46
  # Copyright 2020 Peter Billam
47
  #
48
  ###################################################################################
49
+ ###################################################################################
50
+ '''
51
+
52
+ ###################################################################################
53
+
54
+ __version__ = "25.7.8"
55
+
56
+ print('=' * 70)
57
+ print('TMIDIX Python module')
58
+ print('Version:', __version__)
59
+ print('=' * 70)
60
+ print('Loading module...')
61
+
62
+ ###################################################################################
63
 
64
  import sys, struct, copy
65
 
 
1452
  ###################################################################################
1453
  #
1454
  # Tegridy MIDI X Module (TMIDI X / tee-midi eks)
 
1455
  #
1456
  # Based upon and includes the amazing MIDI.py module v.6.7. by Peter Billam
1457
  # pjb.com.au
 
1488
 
1489
  from collections import Counter
1490
  from collections import defaultdict
1491
+ from collections import OrderedDict
1492
 
1493
  from operator import itemgetter
1494
 
 
1509
 
1510
  import shutil
1511
 
1512
+ import hashlib
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,
 
3901
  else:
3902
  return None
3903
 
3904
+ def fix_monophonic_score_durations(monophonic_score,
3905
+ min_notes_gap=1,
3906
+ min_notes_dur=1
3907
+ ):
3908
 
3909
  fixed_score = []
3910
 
 
3916
  nmt = monophonic_score[i+1][1]
3917
 
3918
  if note[1]+note[2] >= nmt:
3919
+ note_dur = max(1, nmt-note[1]-min_notes_gap)
3920
  else:
3921
  note_dur = note[2]
3922
 
3923
  new_note = [note[0], note[1], note_dur] + note[3:]
3924
+
3925
+ if new_note[2] >= min_notes_dur:
3926
+ fixed_score.append(new_note)
3927
+
3928
+ if monophonic_score[-1][2] >= min_notes_dur:
3929
+ fixed_score.append(monophonic_score[-1])
3930
 
3931
  elif type(monophonic_score[0][0]) == int:
3932
 
 
3936
  nmt = monophonic_score[i+1][0]
3937
 
3938
  if note[0]+note[1] >= nmt:
3939
+ note_dur = max(1, nmt-note[0]-min_notes_gap)
3940
  else:
3941
  note_dur = note[1]
3942
+
3943
  new_note = [note[0], note_dur] + note[2:]
3944
+
3945
+ if new_note[1] >= min_notes_dur:
3946
+ fixed_score.append(new_note)
3947
+
3948
+ if monophonic_score[-1][1] >= min_notes_dur:
3949
+ fixed_score.append(monophonic_score[-1])
3950
 
3951
  return fixed_score
3952
 
 
5062
 
5063
  ###################################################################################
5064
 
5065
+ def patch_enhanced_score_notes(escore_notes,
5066
+ default_patch=0,
5067
+ reserved_patch=-1,
5068
+ reserved_patch_channel=-1,
5069
+ drums_patch=9,
5070
+ verbose=False
5071
+ ):
5072
 
5073
+ #===========================================================================
5074
+
5075
+ enhanced_score_notes = copy.deepcopy(escore_notes)
5076
+
5077
+ #===========================================================================
5078
 
5079
  enhanced_score_notes_with_patch_changes = []
5080
 
5081
  patches = [-1] * 16
5082
 
5083
+ if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
5084
+ patches[reserved_patch_channel] = reserved_patch
5085
+
5086
  overflow_idx = -1
5087
 
5088
  for idx, e in enumerate(enhanced_score_notes):
5089
+ if e[0] == 'note':
5090
+ if e[3] != 9:
5091
+ if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
5092
+ if e[6] == reserved_patch:
5093
+ e[3] = reserved_patch_channel
5094
+
5095
+ if patches[e[3]] == -1:
5096
+ patches[e[3]] = e[6]
5097
+
5098
+ else:
5099
+ if patches[e[3]] != e[6]:
5100
+ if e[6] in patches:
5101
+ e[3] = patches.index(e[6])
5102
+
5103
+ else:
5104
+ if -1 in patches:
5105
+ patches[patches.index(-1)] = e[6]
5106
+
5107
+ else:
5108
+ overflow_idx = idx
5109
+ break
5110
+
5111
+ enhanced_score_notes_with_patch_changes.append(e)
5112
 
5113
  #===========================================================================
5114
 
5115
  overflow_patches = []
5116
+ overflow_channels = [-1] * 16
5117
+ overflow_channels[9] = drums_patch
5118
+
5119
+ if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
5120
+ overflow_channels[reserved_patch_channel] = reserved_patch
5121
 
5122
  if overflow_idx != -1:
5123
+ for idx, e in enumerate(enhanced_score_notes[overflow_idx:]):
5124
+ if e[0] == 'note':
5125
+ if e[3] != 9:
5126
+ if e[6] not in overflow_channels:
5127
+
5128
+ if -1 in overflow_channels:
5129
+ free_chan = overflow_channels.index(-1)
5130
+ overflow_channels[free_chan] = e[6]
5131
+ e[3] = free_chan
5132
+
5133
+ enhanced_score_notes_with_patch_changes.append(['patch_change', e[1], e[3], e[6]])
5134
+
5135
+ overflow_patches.append(e[6])
5136
+
5137
+ else:
5138
+ overflow_channels = [-1] * 16
5139
+ overflow_channels[9] = drums_patch
5140
+
5141
+ if -1 < reserved_patch < 128 and -1 < reserved_patch_channel < 128:
5142
+ overflow_channels[reserved_patch_channel] = reserved_patch
5143
+ e[3] = reserved_patch_channel
5144
+
5145
+ if e[6] != reserved_patch:
5146
+
5147
+ free_chan = overflow_channels.index(-1)
5148
+ e[3] = free_chan
5149
+
5150
+ overflow_channels[e[3]] = e[6]
5151
+
5152
+ enhanced_score_notes_with_patch_changes.append(['patch_change', e[1], e[3], e[6]])
5153
+
5154
+ overflow_patches.append(e[6])
5155
+
5156
+ else:
5157
+ e[3] = overflow_channels.index(e[6])
5158
 
5159
+ enhanced_score_notes_with_patch_changes.append(e)
5160
 
5161
  #===========================================================================
5162
 
 
5166
 
5167
  #===========================================================================
5168
 
5169
+ overflow_patches = ordered_set(overflow_patches)
5170
+
5171
+ #===========================================================================
5172
+
5173
  if verbose:
5174
  print('=' * 70)
5175
+ print('Main composition patches')
5176
  print('=' * 70)
5177
  for c, p in enumerate(patches):
5178
  print('Cha', str(c).zfill(2), '---', str(p).zfill(3), Number2patch[p])
 
5185
  print(str(p).zfill(3), Number2patch[p])
5186
  print('=' * 70)
5187
 
5188
+ #===========================================================================
5189
+
5190
  return enhanced_score_notes_with_patch_changes, patches, overflow_patches
5191
 
5192
  ###################################################################################
 
11282
 
11283
  ###################################################################################
11284
 
11285
+ def multiprocessing_wrapper(function, data_list, verbose=True):
11286
 
11287
  with multiprocessing.Pool() as pool:
11288
 
11289
  results = []
11290
 
11291
+ for result in tqdm.tqdm(pool.imap_unordered(function, data_list),
11292
+ total=len(data_list),
11293
+ disable=not verbose
11294
+ ):
11295
+
11296
  results.append(result)
11297
 
11298
  return results
 
11441
 
11442
  ###################################################################################
11443
 
11444
+ def system_cpus_utilization(return_dict=False):
11445
+
11446
+ if return_dict:
11447
+ return {'num_cpus': psutil.cpu_count(),
11448
+ 'cpus_util': psutil.cpu_percent()
11449
+ }
11450
+
11451
+ else:
11452
+ print('Number of CPUs:', psutil.cpu_count())
11453
+ print('CPUs utilization:', psutil.cpu_percent())
11454
+
11455
+ ###################################################################################
11456
+
11457
  def create_files_list(datasets_paths=['./'],
11458
  files_exts=['.mid', '.midi', '.kar', '.MID', '.MIDI', '.KAR'],
11459
+ max_num_files_per_dir=-1,
11460
+ randomize_dir_files=False,
11461
+ max_total_files=-1,
11462
  randomize_files_list=True,
11463
+ check_for_dupes=False,
11464
+ use_md5_hashes=False,
11465
+ return_dupes=False,
11466
  verbose=True
11467
  ):
11468
+
11469
  if verbose:
11470
  print('=' * 70)
11471
  print('Searching for files...')
11472
  print('This may take a while on a large dataset in particular...')
11473
  print('=' * 70)
11474
 
 
 
11475
  files_exts = tuple(files_exts)
11476
 
11477
+ filez_set = defaultdict(None)
11478
+ dupes_list = []
11479
+
11480
+ for dataset_addr in datasets_paths:
11481
+
11482
+ print('=' * 70)
11483
+ print('Processing', dataset_addr)
11484
+ print('=' * 70)
11485
+
11486
+ for dirpath, dirnames, filenames in tqdm.tqdm(os.walk(dataset_addr), disable=not verbose):
11487
+
11488
+ if randomize_dir_files:
11489
+ random.shuffle(filenames)
11490
+
11491
+ if max_num_files_per_dir > 0:
11492
+ max_num_files = max_num_files_per_dir
11493
+
11494
+ else:
11495
+ max_num_files = len(filenames)
11496
+
11497
+ for file in filenames[:max_num_files]:
11498
+ if file.endswith(files_exts):
11499
+ if check_for_dupes:
11500
+
11501
+ if use_md5_hashes:
11502
+ md5_hash = hashlib.md5(open(os.path.join(dirpath, file), 'rb').read()).hexdigest()
11503
+
11504
+ if md5_hash not in filez_set:
11505
+ filez_set[md5_hash] = os.path.join(dirpath, file)
11506
+
11507
+ else:
11508
+ dupes_list.append(os.path.join(dirpath, file))
11509
+
11510
+ else:
11511
+ if file not in filez_set:
11512
+ filez_set[file] = os.path.join(dirpath, file)
11513
+
11514
+ else:
11515
+ dupes_list.append(os.path.join(dirpath, file))
11516
+ else:
11517
+ fpath = os.path.join(dirpath, file)
11518
+ filez_set[fpath] = fpath
11519
+
11520
+ filez = list(filez_set.values())
11521
 
11522
  if verbose:
11523
  print('Done!')
 
11537
 
11538
  if verbose:
11539
  print('Found', len(filez), 'files.')
11540
+ print('Skipped', len(dupes_list), 'duplicate files.')
11541
  print('=' * 70)
11542
 
11543
  else:
 
11545
  print('Could not find any files...')
11546
  print('Please check dataset dirs and files extensions...')
11547
  print('=' * 70)
11548
+
11549
+ if max_total_files > 0:
11550
+ if return_dupes:
11551
+ return filez[:max_total_files], dupes_list
11552
 
11553
+ else:
11554
+ return filez[:max_total_files]
11555
+
11556
+ else:
11557
+ if return_dupes:
11558
+ return filez, dupes_list
11559
+
11560
+ else:
11561
+ return filez
11562
 
11563
  ###################################################################################
11564
 
 
12380
  use_full_chords=False
12381
  ):
12382
 
12383
+ if use_full_chords:
12384
+ CHORDS = ALL_CHORDS_FULL
12385
+
12386
+ else:
12387
+ CHORDS = ALL_CHORDS_SORTED
12388
+
12389
  max_patch = max(0, min(128, max_patch))
12390
 
12391
  escore_notes = [e for e in escore_notes if e[6] <= max_patch]
 
12397
  sig = []
12398
  dsig = []
12399
 
12400
+ drums_offset = len(CHORDS) + 128
12401
 
12402
  bad_chords_counter = 0
12403
 
 
12414
  tones_chord = sorted(set([p % 12 for p in pitches]))
12415
 
12416
  try:
12417
+ sig_token = CHORDS.index(tones_chord) + 128
12418
  except:
12419
  checked_tones_chord = check_and_fix_tones_chord(tones_chord, use_full_chords=use_full_chords)
12420
+ sig_token = CHORDS.index(checked_tones_chord) + 128
12421
  bad_chords_counter += 1
12422
 
12423
  elif len(pitches) == 1:
 
12628
  e[1] = mchord[0][1]
12629
 
12630
  if sort_drums_last:
12631
+ mchord.sort(key=lambda x: (-x[4], x[6]) if x[6] != 128 else (x[6], -x[4]))
12632
 
12633
  else:
12634
+ mchord.sort(key=lambda x: (-x[4], x[6]))
12635
 
12636
  return mchord
12637
 
 
12943
 
12944
  return melody_chords
12945
 
12946
+ ###################################################################################
12947
+
12948
+ def merge_counts(data, return_lists=True):
12949
+
12950
+ merged = defaultdict(int)
12951
+
12952
+ for value, count in data:
12953
+ merged[value] += count
12954
+
12955
+ if return_lists:
12956
+ return [[k, v] for k, v in merged.items()]
12957
+
12958
+ else:
12959
+ return list(merged.items())
12960
+
12961
+ ###################################################################################
12962
+
12963
+ def convert_escore_notes_pitches_chords_signature(signature, convert_to_full_chords=True):
12964
+
12965
+ if convert_to_full_chords:
12966
+ SRC_CHORDS = ALL_CHORDS_SORTED
12967
+ TRG_CHORDS = ALL_CHORDS_FULL
12968
+
12969
+ else:
12970
+ SRC_CHORDS = ALL_CHORDS_FULL
12971
+ TRG_CHORDS = ALL_CHORDS_SORTED
12972
+
12973
+ cdiff = len(TRG_CHORDS) - len(SRC_CHORDS)
12974
+
12975
+ pitches_counts = [c for c in signature if -1 < c[0] < 128]
12976
+ chords_counts = [c for c in signature if 127 < c[0] < len(SRC_CHORDS)+128]
12977
+ drums_counts = [[c[0]+cdiff, c[1]] for c in signature if len(SRC_CHORDS)+127 < c[0] < len(SRC_CHORDS)+256]
12978
+ bad_chords_count = [c for c in signature if c[0] == -1]
12979
+
12980
+ new_chords_counts = []
12981
+
12982
+ for c in chords_counts:
12983
+ tones_chord = SRC_CHORDS[c[0]-128]
12984
+
12985
+ if tones_chord not in TRG_CHORDS:
12986
+ tones_chord = check_and_fix_tones_chord(tones_chord, use_full_chords=convert_to_full_chords)
12987
+ bad_chords_count[0][1] += 1
12988
+
12989
+ new_chords_counts.append([TRG_CHORDS.index(tones_chord)+128, c[1]])
12990
+
12991
+ return pitches_counts + merge_counts(new_chords_counts) + drums_counts + bad_chords_count
12992
+
12993
+ ###################################################################################
12994
+
12995
+ def convert_bytes_in_nested_list(lst, encoding='utf-8', errors='ignore'):
12996
+
12997
+ new_list = []
12998
+
12999
+ for item in lst:
13000
+ if isinstance(item, list):
13001
+ new_list.append(convert_bytes_in_nested_list(item))
13002
+
13003
+ elif isinstance(item, bytes):
13004
+ new_list.append(item.decode(encoding, errors=errors))
13005
+
13006
+ else:
13007
+ new_list.append(item)
13008
+
13009
+ return new_list
13010
+
13011
+ ###################################################################################
13012
+
13013
+ def mult_pitches(pitches, min_oct=4, max_oct=6):
13014
+
13015
+ tones_chord = sorted(set([p % 12 for p in pitches]))
13016
+
13017
+ mult_ptcs = []
13018
+
13019
+ for t in tones_chord:
13020
+ for i in range(min_oct, max_oct):
13021
+ mult_ptcs.append((i*12)+t)
13022
+
13023
+ return mult_ptcs
13024
+
13025
+ ###################################################################################
13026
+
13027
+ def find_next(pitches, cur_ptc):
13028
+
13029
+ i = 0
13030
+
13031
+ for i, p in enumerate(pitches):
13032
+ if p != cur_ptc:
13033
+ break
13034
+
13035
+ return i
13036
+
13037
+ ###################################################################################
13038
+
13039
+ def ordered_groups_unsorted(data, key_index):
13040
+
13041
+ def keyfunc(sublist):
13042
+ return sublist[key_index]
13043
+
13044
+ groups = []
13045
+
13046
+ for key, group in groupby(data, key=keyfunc):
13047
+ groups.append((key, list(group)))
13048
+
13049
+ return groups
13050
+
13051
+ ###################################################################################
13052
+
13053
+ def ordered_groups(data, ptc_idx, pat_idx):
13054
+
13055
+ groups = OrderedDict()
13056
+
13057
+ for sublist in data:
13058
+ key = tuple([sublist[ptc_idx], sublist[pat_idx]])
13059
+
13060
+ if key not in groups:
13061
+ groups[key] = []
13062
+
13063
+ groups[key].append(sublist)
13064
+
13065
+ return list(groups.items())
13066
+
13067
+ ###################################################################################
13068
+
13069
+ def merge_melody_notes(escore_notes, pitches_idx=4, max_dur=255, last_dur=128):
13070
+
13071
+ groups = ordered_groups_unsorted(escore_notes, pitches_idx)
13072
+
13073
+ merged_melody_notes = []
13074
+
13075
+ for i, (k, g) in enumerate(groups[:-1]):
13076
+
13077
+ if len(g) == 1:
13078
+ merged_melody_notes.extend(g)
13079
+
13080
+ else:
13081
+ dur = min(max_dur, groups[i+1][1][0][1] - g[0][1])
13082
+
13083
+ merged_melody_notes.append(['note',
13084
+ g[0][1],
13085
+ dur,
13086
+ g[0][3],
13087
+ g[0][4],
13088
+ g[0][5],
13089
+ g[0][6]
13090
+ ])
13091
+
13092
+ merged_melody_notes.append(['note',
13093
+ groups[-1][1][0][1],
13094
+ last_dur,
13095
+ groups[-1][1][0][3],
13096
+ groups[-1][1][0][4],
13097
+ groups[-1][1][0][5],
13098
+ groups[-1][1][0][6]
13099
+ ])
13100
+
13101
+ return merged_melody_notes
13102
+
13103
+ ###################################################################################
13104
+
13105
+ def add_expressive_melody_to_enhanced_score_notes(escore_notes,
13106
+ melody_start_chord=0,
13107
+ melody_prime_pitch=60,
13108
+ melody_step=1,
13109
+ melody_channel=3,
13110
+ melody_patch=40,
13111
+ melody_notes_max_duration=255,
13112
+ melody_last_note_dur=128,
13113
+ melody_clip_max_min_durs=[],
13114
+ melody_max_velocity=120,
13115
+ acc_max_velocity=90,
13116
+ return_melody=False
13117
+ ):
13118
+
13119
+
13120
+ score = copy.deepcopy(escore_notes)
13121
+
13122
+ adjust_score_velocities(score, acc_max_velocity)
13123
+
13124
+ cscore = chordify_score([1000, score])
13125
+
13126
+ melody_pitches = [melody_prime_pitch]
13127
+
13128
+ for i, c in enumerate(cscore[melody_start_chord:]):
13129
+
13130
+ if i % melody_step == 0:
13131
+
13132
+ pitches = [e[4] for e in c if e[3] != 9]
13133
+
13134
+ if pitches:
13135
+ cptc = find_closest_value(mult_pitches(pitches), melody_pitches[-1])[0]
13136
+ melody_pitches.append(cptc)
13137
+
13138
+ song_f = []
13139
+ mel_f = []
13140
+
13141
+ idx = 1
13142
+
13143
+ for i, c in enumerate(cscore[:-melody_step]):
13144
+ pitches = [e[4] for e in c if e[3] != 9]
13145
+
13146
+ if pitches and i >= melody_start_chord and i % melody_step == 0:
13147
+ dur = min(cscore[i+melody_step][0][1] - c[0][1], melody_notes_max_duration)
13148
+
13149
+ mel_f.append(['note',
13150
+ c[0][1],
13151
+ dur,
13152
+ melody_channel,
13153
+ 60+(melody_pitches[idx] % 24),
13154
+ 100 + ((melody_pitches[idx] % 12) * 2),
13155
+ melody_patch
13156
+ ])
13157
+ idx += 1
13158
+
13159
+ song_f.extend(c)
13160
+
13161
+ song_f.extend(flatten(cscore[-melody_step:]))
13162
+
13163
+ if len(melody_clip_max_min_durs) == 2:
13164
+ for e in mel_f:
13165
+ if e[2] >= melody_clip_max_min_durs[0]:
13166
+ e[2] = melody_clip_max_min_durs[1]
13167
+
13168
+ adjust_score_velocities(mel_f, melody_max_velocity)
13169
+
13170
+ merged_melody_notes = merge_melody_notes(mel_f,
13171
+ max_dur=melody_notes_max_duration,
13172
+ last_dur=melody_last_note_dur
13173
+ )
13174
+
13175
+ song_f = sorted(merged_melody_notes + song_f,
13176
+ key=lambda x: x[1]
13177
+ )
13178
+
13179
+ if return_melody:
13180
+ return mel_f
13181
+
13182
+ else:
13183
+ return song_f
13184
+
13185
+ ###################################################################################
13186
+
13187
+ def list_md5_hash(ints_list):
13188
+
13189
+ arr = array('H', ints_list)
13190
+ binary_data = arr.tobytes()
13191
+
13192
+ return hashlib.md5(binary_data).hexdigest()
13193
+
13194
+ ###################################################################################
13195
+
13196
+ def fix_escore_notes_durations(escore_notes,
13197
+ min_notes_gap=1,
13198
+ min_notes_dur=1,
13199
+ times_idx=1,
13200
+ durs_idx=2,
13201
+ channels_idx = 3,
13202
+ pitches_idx=4,
13203
+ patches_idx=6
13204
+ ):
13205
+
13206
+ notes = [e for e in escore_notes if e[channels_idx] != 9]
13207
+ drums = [e for e in escore_notes if e[channels_idx] == 9]
13208
+
13209
+ escore_groups = ordered_groups(notes, pitches_idx, patches_idx)
13210
+
13211
+ merged_score = []
13212
+
13213
+ for k, g in escore_groups:
13214
+ if len(g) > 2:
13215
+ fg = fix_monophonic_score_durations(g,
13216
+ min_notes_gap=min_notes_gap,
13217
+ min_notes_dur=min_notes_dur
13218
+ )
13219
+ merged_score.extend(fg)
13220
+
13221
+ elif len(g) == 2:
13222
+
13223
+ if g[0][times_idx]+g[0][durs_idx] >= g[1][times_idx]:
13224
+ g[0][durs_idx] = max(1, g[1][times_idx] - g[0][times_idx] - min_notes_gap)
13225
+
13226
+ merged_score.extend(g)
13227
+
13228
+ else:
13229
+ merged_score.extend(g)
13230
+
13231
+ return sorted(merged_score + drums, key=lambda x: x[times_idx])
13232
+
13233
+ ###################################################################################
13234
+
13235
+ def create_nested_chords_tree(chords_list):
13236
+
13237
+ tree = {}
13238
+
13239
+ for chord in chords_list:
13240
+
13241
+ node = tree
13242
+
13243
+ for semitone in chord:
13244
+ if semitone not in node:
13245
+ node[semitone] = {}
13246
+
13247
+ node = node[semitone]
13248
+
13249
+ node.setdefault(-1, []).append(chord)
13250
+
13251
+ return tree
13252
+
13253
+ ###################################################################################
13254
+
13255
+ def get_chords_with_prefix(nested_chords_tree, prefix):
13256
+
13257
+ node = nested_chords_tree
13258
+
13259
+ for semitone in prefix:
13260
+ if semitone in node:
13261
+ node = node[semitone]
13262
+
13263
+ else:
13264
+ return []
13265
+
13266
+ collected_chords = []
13267
+
13268
+ def recursive_collect(subnode):
13269
+ if -1 in subnode:
13270
+ collected_chords.extend(subnode[-1])
13271
+
13272
+ for key, child in subnode.items():
13273
+ if key != -1:
13274
+ recursive_collect(child)
13275
+
13276
+ recursive_collect(node)
13277
+
13278
+ return collected_chords
13279
+
13280
+ ###################################################################################
13281
+
13282
+ def get_chords_by_semitones(chords_list, chord_semitones):
13283
+
13284
+ query_set = set(chord_semitones)
13285
+ results = []
13286
+
13287
+ for chord in chords_list:
13288
+
13289
+ chord_set = set(chord)
13290
+
13291
+ if query_set.issubset(chord_set):
13292
+ results.append(sorted(set(chord)))
13293
+
13294
+ return results
13295
+
13296
+ ###################################################################################
13297
+
13298
+ def remove_duplicate_pitches_from_escore_notes(escore_notes,
13299
+ pitches_idx=4,
13300
+ patches_idx=6,
13301
+ return_dupes_count=False
13302
+ ):
13303
+
13304
+ cscore = chordify_score([1000, escore_notes])
13305
+
13306
+ new_escore = []
13307
+
13308
+ bp_count = 0
13309
+
13310
+ for c in cscore:
13311
+
13312
+ cho = []
13313
+ seen = []
13314
+
13315
+ for cc in c:
13316
+ if [cc[pitches_idx], cc[patches_idx]] not in seen:
13317
+ cho.append(cc)
13318
+ seen.append([cc[pitches_idx], cc[patches_idx]])
13319
+
13320
+ else:
13321
+ bp_count += 1
13322
+
13323
+ new_escore.extend(cho)
13324
+
13325
+ if return_dupes_count:
13326
+ return bp_count
13327
+
13328
+ else:
13329
+ return new_escore
13330
+
13331
+ ###################################################################################
13332
+
13333
+ def chunks_shuffle(lst,
13334
+ min_len=1,
13335
+ max_len=3,
13336
+ seed=None
13337
+ ):
13338
+
13339
+ rnd = random.Random(seed)
13340
+ chunks = []
13341
+ i, n = 0, len(lst)
13342
+
13343
+ while i < n:
13344
+ size = rnd.randint(min_len, max_len)
13345
+ size = min(size, n - i)
13346
+ chunks.append(lst[i : i + size])
13347
+ i += size
13348
+
13349
+ rnd.shuffle(chunks)
13350
+
13351
+ flattened = []
13352
+ for chunk in chunks:
13353
+ flattened.extend(chunk)
13354
+
13355
+ return flattened
13356
+
13357
+ ###################################################################################
13358
+
13359
+ def convert_bytes_in_nested_list(lst,
13360
+ encoding='utf-8',
13361
+ errors='ignore',
13362
+ return_changed_events_count=False
13363
+ ):
13364
+
13365
+ new_list = []
13366
+
13367
+ ce_count = 0
13368
+
13369
+ for item in lst:
13370
+ if isinstance(item, list):
13371
+ new_list.append(convert_bytes_in_nested_list(item))
13372
+
13373
+ elif isinstance(item, bytes):
13374
+ new_list.append(item.decode(encoding, errors=errors))
13375
+ ce_count += 1
13376
+
13377
+ else:
13378
+ new_list.append(item)
13379
+
13380
+ if return_changed_events_count:
13381
+ return new_list, ce_count
13382
+
13383
+ else:
13384
+ return new_list
13385
+
13386
+ ###################################################################################
13387
+
13388
+ def find_deepest_midi_dirs(roots,
13389
+ marker_file="midi_score.mid",
13390
+ suffixes=None,
13391
+ randomize=False,
13392
+ seed=None,
13393
+ verbose=False
13394
+ ):
13395
+
13396
+ try:
13397
+ iter(roots)
13398
+ if isinstance(roots, (str, Path)):
13399
+ root_list = [roots]
13400
+ else:
13401
+ root_list = list(roots)
13402
+
13403
+ except TypeError:
13404
+ root_list = [roots]
13405
+
13406
+ if isinstance(marker_file, (list, tuple)):
13407
+ patterns = [p.lower() for p in marker_file if p]
13408
+
13409
+ else:
13410
+ patterns = [marker_file.lower()] if marker_file else []
13411
+
13412
+ allowed = {s.lower() for s in (suffixes or ['.mid', '.midi', '.kar'])}
13413
+
13414
+ if verbose:
13415
+ print("Settings:")
13416
+ print(" Roots:", [str(r) for r in root_list])
13417
+ print(" Marker patterns:", patterns or "<no marker filter>")
13418
+ print(" Allowed suffixes:", allowed)
13419
+ print(f" Randomize={randomize}, Seed={seed}")
13420
+
13421
+ results = defaultdict(list)
13422
+ rng = random.Random(seed)
13423
+
13424
+ for root in root_list:
13425
+
13426
+ root_path = Path(root)
13427
+
13428
+ if not root_path.is_dir():
13429
+ print(f"Warning: '{root_path}' is not a valid directory, skipping.")
13430
+ continue
13431
+
13432
+ if verbose:
13433
+ print(f"\nScanning root: {str(root_path)}")
13434
+
13435
+ all_dirs = list(root_path.rglob("*"))
13436
+ dirs_iter = tqdm.tqdm(all_dirs, desc=f"Dirs in {root_path.name}", disable=not verbose)
13437
+
13438
+ for dirpath in dirs_iter:
13439
+ if not dirpath.is_dir():
13440
+ continue
13441
+
13442
+ children = list(dirpath.iterdir())
13443
+ if any(child.is_dir() for child in children):
13444
+ if verbose:
13445
+ print(f"Skipping non-leaf: {str(dirpath)}")
13446
+ continue
13447
+
13448
+ files = [f for f in children if f.is_file()]
13449
+ names = [f.name.lower() for f in files]
13450
+
13451
+ if patterns:
13452
+ matched = any(fnmatch(name, pat) for name in names for pat in patterns)
13453
+ if not matched:
13454
+ if verbose:
13455
+ print(f"No marker in: {str(dirpath)}")
13456
+ continue
13457
+
13458
+ if verbose:
13459
+ print(f"Marker found in: {str(dirpath)}")
13460
+
13461
+ else:
13462
+ if verbose:
13463
+ print(f"Including leaf (no marker): {str(dirpath)}")
13464
+
13465
+ for f in files:
13466
+ if f.suffix.lower() in allowed:
13467
+ results[str(dirpath)].append(str(f))
13468
+
13469
+ if verbose:
13470
+ print(f" Collected: {f.name}")
13471
+
13472
+ all_leaves = list(results.keys())
13473
+ if randomize:
13474
+ if verbose:
13475
+ print("\nShuffling leaf directories")
13476
+
13477
+ rng.shuffle(all_leaves)
13478
+
13479
+ else:
13480
+ all_leaves.sort()
13481
+
13482
+ final_dict = {}
13483
+
13484
+ for leaf in all_leaves:
13485
+ file_list = results[leaf][:]
13486
+ if randomize:
13487
+ if verbose:
13488
+ print(f"Shuffling files in: {leaf}")
13489
+
13490
+ rng.shuffle(file_list)
13491
+
13492
+ else:
13493
+ file_list.sort()
13494
+
13495
+ final_dict[leaf] = file_list
13496
+
13497
+ if verbose:
13498
+ print("\nScan complete. Found directories:")
13499
+ for d, fl in final_dict.items():
13500
+ print(f" {d} -> {len(fl)} files")
13501
+
13502
+ return final_dict
13503
+
13504
+ ###################################################################################
13505
+
13506
+ PERCUSSION_GROUPS = {
13507
+
13508
+ 1: { # Bass Drums
13509
+ 35: 'Acoustic Bass Drum',
13510
+ 36: 'Bass Drum 1',
13511
+ },
13512
+ 2: { # Stick
13513
+ 37: 'Side Stick',
13514
+ },
13515
+ 3: { # Snares
13516
+ 38: 'Acoustic Snare',
13517
+ 40: 'Electric Snare',
13518
+ },
13519
+ 4: { # Claps
13520
+ 39: 'Hand Clap',
13521
+ },
13522
+ 5: { # Floor Toms
13523
+ 41: 'Low Floor Tom',
13524
+ 43: 'High Floor Tom',
13525
+ },
13526
+ 6: { # Hi-Hats
13527
+ 42: 'Closed Hi-Hat',
13528
+ 44: 'Pedal Hi-Hat',
13529
+ 46: 'Open Hi-Hat',
13530
+ },
13531
+ 7: { # Toms
13532
+ 45: 'Low Tom',
13533
+ 47: 'Low-Mid Tom',
13534
+ 48: 'Hi-Mid Tom',
13535
+ 50: 'High Tom',
13536
+ },
13537
+ 8: { # Cymbals
13538
+ 49: 'Crash Cymbal 1',
13539
+ 51: 'Ride Cymbal 1',
13540
+ 52: 'Chinese Cymbal',
13541
+ 55: 'Splash Cymbal',
13542
+ 57: 'Crash Cymbal 2',
13543
+ 59: 'Ride Cymbal 2',
13544
+ },
13545
+ 9: { # Bells
13546
+ 53: 'Ride Bell',
13547
+ },
13548
+ 10: { # Tambourine
13549
+ 54: 'Tambourine',
13550
+ },
13551
+ 11: { # Cowbell
13552
+ 56: 'Cowbell',
13553
+ },
13554
+ 12: { # Vibraslap
13555
+ 58: 'Vibraslap',
13556
+ },
13557
+ 13: { # Bongos
13558
+ 60: 'Hi Bongo',
13559
+ 61: 'Low Bongo',
13560
+ },
13561
+ 14: { # Congas
13562
+ 62: 'Mute Hi Conga',
13563
+ 63: 'Open Hi Conga',
13564
+ 64: 'Low Conga',
13565
+ },
13566
+ 15: { # Timbales
13567
+ 65: 'High Timbale',
13568
+ 66: 'Low Timbale',
13569
+ },
13570
+ 16: { # Agog么
13571
+ 67: 'High Agogo',
13572
+ 68: 'Low Agogo',
13573
+ },
13574
+ 17: { # Cabasa
13575
+ 69: 'Cabasa',
13576
+ },
13577
+ 18: { # Maracas
13578
+ 70: 'Maracas',
13579
+ },
13580
+ 19: { # Whistles
13581
+ 71: 'Short Whistle',
13582
+ 72: 'Long Whistle',
13583
+ },
13584
+ 20: { # Guiros
13585
+ 73: 'Short Guiro',
13586
+ 74: 'Long Guiro',
13587
+ },
13588
+ 21: { # Claves
13589
+ 75: 'Claves',
13590
+ },
13591
+ 22: { # Wood Blocks
13592
+ 76: 'Hi Wood Block',
13593
+ 77: 'Low Wood Block',
13594
+ },
13595
+ 23: { # Cuica
13596
+ 78: 'Mute Cuica',
13597
+ 79: 'Open Cuica',
13598
+ },
13599
+ 24: { # Triangles
13600
+ 80: 'Mute Triangle',
13601
+ 81: 'Open Triangle',
13602
+ },
13603
+ }
13604
+
13605
+ ###################################################################################
13606
+
13607
+ print('Module loaded!')
13608
+ print('=' * 70)
13609
+ print('Enjoy! :)')
13610
+ print('=' * 70)
13611
+
13612
  ###################################################################################
13613
  # This is the end of the TMIDI X Python module
13614
  ###################################################################################