asigalov61 commited on
Commit
791ed90
·
verified ·
1 Parent(s): 21fb761

Upload TMIDIX.py

Browse files
Files changed (1) hide show
  1. TMIDIX.py +602 -19
TMIDIX.py CHANGED
@@ -14,14 +14,14 @@ r'''############################################################################
14
  #
15
  # Project Los Angeles
16
  #
17
- # Tegridy Code 2021
18
  #
19
  # https://github.com/Tegridy-Code/Project-Los-Angeles
20
  #
21
  #
22
  ###################################################################################
23
  ###################################################################################
24
- # Copyright 2021 Project Los Angeles / Tegridy Code
25
  #
26
  # Licensed under the Apache License, Version 2.0 (the "License");
27
  # you may not use this file except in compliance with the License.
@@ -7101,13 +7101,25 @@ def escore_notes_averages(escore_notes,
7101
  else:
7102
  durs = [e[durs_index] for e in escore_notes if e[chans_index] != 9]
7103
 
 
 
 
 
 
 
7104
  if return_ptcs_and_vels:
7105
  if average_drums:
7106
  ptcs = [e[ptcs_index] for e in escore_notes]
7107
  vels = [e[vels_index] for e in escore_notes]
7108
  else:
7109
  ptcs = [e[ptcs_index] for e in escore_notes if e[chans_index] != 9]
7110
- vels = [e[vels_index] for e in escore_notes if e[chans_index] != 9]
 
 
 
 
 
 
7111
 
7112
  return [sum(times) / len(times), sum(durs) / len(durs), sum(ptcs) / len(ptcs), sum(vels) / len(vels)]
7113
 
@@ -9540,15 +9552,20 @@ MOOD_SCALES = ['Major', 'Minor', 'Major Minor']
9540
  ###################################################################################
9541
 
9542
  def alpha_str(string):
9543
- return re.sub(r'[^a-zA-Z ()]', '', string).strip()
 
9544
 
9545
  ###################################################################################
9546
 
9547
- def escore_notes_to_text_description(escore_notes, song_name='', artist_name=''):
 
 
 
 
9548
 
9549
  #==============================================================================
9550
 
9551
- song_time_min = (escore_notes[-1][1] * 16) / 1000 / 60
9552
 
9553
  if song_time_min < 1.5:
9554
  song_length = 'short'
@@ -9567,23 +9584,29 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
9567
 
9568
  if len(escore_times) > 0:
9569
  if len(escore_times) == len(set(escore_times)):
9570
- comp_type = 'melody only'
9571
 
9572
  elif len(escore_times) >= len(set(escore_times)) and 1 in Counter(escore_times).values():
9573
  comp_type = 'melody and accompaniment'
9574
 
9575
  elif len(escore_times) >= len(set(escore_times)) and 1 not in Counter(escore_times).values():
9576
- comp_type = 'accompaniment only'
9577
 
9578
  else:
9579
  comp_type = 'drums only'
9580
 
9581
  #==============================================================================
9582
 
9583
- patches = sorted(set([e[6] for e in escore_notes]))
 
 
9584
 
9585
  instruments = [alpha_str(Number2patch[p]) for p in patches if p < 128]
9586
 
 
 
 
 
9587
  if 128 in patches:
9588
  drums_present = True
9589
 
@@ -9592,7 +9615,7 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
9592
 
9593
  drums_pitches = [e[4] for e in escore_notes if e[3] == 9]
9594
 
9595
- most_common_drums = [alpha_str(Notenum2percussion[p[0]]) for p in Counter(drums_pitches).most_common(3)]
9596
 
9597
  #==============================================================================
9598
 
@@ -9629,22 +9652,22 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
9629
 
9630
  escore_averages = escore_notes_averages(escore_notes, return_ptcs_and_vels=True)
9631
 
9632
- if escore_averages[0] < 8:
9633
  rythm = 'fast'
9634
 
9635
- elif 8 <= escore_averages[0] <= 12:
9636
  rythm = 'average'
9637
 
9638
- elif escore_averages[0] > 12:
9639
  rythm = 'slow'
9640
 
9641
- if escore_averages[1] < 16:
9642
  tempo = 'fast'
9643
 
9644
- elif 16 <= escore_averages[1] <= 24:
9645
  tempo = 'average'
9646
 
9647
- elif escore_averages[1] > 24:
9648
  tempo = 'slow'
9649
 
9650
  if escore_averages[2] < 50:
@@ -9691,7 +9714,7 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
9691
 
9692
  description += comp_type + ' composition'
9693
 
9694
- if comp_type != 'drums only':
9695
 
9696
  if drums_present:
9697
  description += ' with drums'
@@ -9723,19 +9746,579 @@ def escore_notes_to_text_description(escore_notes, song_name='', artist_name='')
9723
  description += 'is played on a solo ' + instruments[0] + '.'
9724
 
9725
  else:
9726
- description += 'features ' + NUMERALS[len(instruments)-1] + ' instruments: '
9727
  description += ', '.join(instruments[:-1]) + ' and ' + instruments[-1] + '.'
9728
 
9729
  description += '\n'
9730
 
9731
- if drums_present:
 
 
 
 
 
 
 
 
9732
  description += 'The drum track has predominant '
9733
  description += ', '.join(most_common_drums[:-1]) + ' and ' + most_common_drums[-1] + '.'
9734
 
 
 
9735
  #==============================================================================
9736
 
9737
  return description
9738
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9739
  ###################################################################################
9740
  #
9741
  # This is the end of the TMIDI X Python module
 
14
  #
15
  # Project Los Angeles
16
  #
17
+ # Tegridy Code 2024
18
  #
19
  # https://github.com/Tegridy-Code/Project-Los-Angeles
20
  #
21
  #
22
  ###################################################################################
23
  ###################################################################################
24
+ # Copyright 2024 Project Los Angeles / Tegridy Code
25
  #
26
  # Licensed under the Apache License, Version 2.0 (the "License");
27
  # you may not use this file except in compliance with the License.
 
7101
  else:
7102
  durs = [e[durs_index] for e in escore_notes if e[chans_index] != 9]
7103
 
7104
+ if len(times) == 0:
7105
+ times = [0]
7106
+
7107
+ if len(durs) == 0:
7108
+ durs = [0]
7109
+
7110
  if return_ptcs_and_vels:
7111
  if average_drums:
7112
  ptcs = [e[ptcs_index] for e in escore_notes]
7113
  vels = [e[vels_index] for e in escore_notes]
7114
  else:
7115
  ptcs = [e[ptcs_index] for e in escore_notes if e[chans_index] != 9]
7116
+ vels = [e[vels_index] for e in escore_notes if e[chans_index] != 9]
7117
+
7118
+ if len(ptcs) == 0:
7119
+ ptcs = [0]
7120
+
7121
+ if len(vels) == 0:
7122
+ vels = [0]
7123
 
7124
  return [sum(times) / len(times), sum(durs) / len(durs), sum(ptcs) / len(ptcs), sum(vels) / len(vels)]
7125
 
 
9552
  ###################################################################################
9553
 
9554
  def alpha_str(string):
9555
+ astr = re.sub(r'[^a-zA-Z ()]', '', string).strip()
9556
+ return re.sub(r'\s+', ' ', astr).strip()
9557
 
9558
  ###################################################################################
9559
 
9560
+ def escore_notes_to_text_description(escore_notes,
9561
+ song_name='',
9562
+ artist_name='',
9563
+ timings_divider=16,
9564
+ ):
9565
 
9566
  #==============================================================================
9567
 
9568
+ song_time_min = (escore_notes[-1][1] * timings_divider) / 1000 / 60
9569
 
9570
  if song_time_min < 1.5:
9571
  song_length = 'short'
 
9584
 
9585
  if len(escore_times) > 0:
9586
  if len(escore_times) == len(set(escore_times)):
9587
+ comp_type = 'monophonic melody'
9588
 
9589
  elif len(escore_times) >= len(set(escore_times)) and 1 in Counter(escore_times).values():
9590
  comp_type = 'melody and accompaniment'
9591
 
9592
  elif len(escore_times) >= len(set(escore_times)) and 1 not in Counter(escore_times).values():
9593
+ comp_type = 'accompaniment'
9594
 
9595
  else:
9596
  comp_type = 'drums only'
9597
 
9598
  #==============================================================================
9599
 
9600
+ all_patches = [e[6] for e in escore_notes]
9601
+
9602
+ patches = ordered_set(all_patches)
9603
 
9604
  instruments = [alpha_str(Number2patch[p]) for p in patches if p < 128]
9605
 
9606
+ nd_patches_counts = Counter([p for p in all_patches if p < 128]).most_common()
9607
+
9608
+ dominant_instrument = alpha_str(Number2patch[nd_patches_counts[0][0]])
9609
+
9610
  if 128 in patches:
9611
  drums_present = True
9612
 
 
9615
 
9616
  drums_pitches = [e[4] for e in escore_notes if e[3] == 9]
9617
 
9618
+ most_common_drums = [alpha_str(Notenum2percussion[p[0]]) for p in Counter(drums_pitches).most_common(3) if p[0] in Notenum2percussion]
9619
 
9620
  #==============================================================================
9621
 
 
9652
 
9653
  escore_averages = escore_notes_averages(escore_notes, return_ptcs_and_vels=True)
9654
 
9655
+ if escore_averages[0] < (128 / timings_divider):
9656
  rythm = 'fast'
9657
 
9658
+ elif (128 / timings_divider) <= escore_averages[0] <= (192 / timings_divider):
9659
  rythm = 'average'
9660
 
9661
+ elif escore_averages[0] > (192 / timings_divider):
9662
  rythm = 'slow'
9663
 
9664
+ if escore_averages[1] < (256 / timings_divider):
9665
  tempo = 'fast'
9666
 
9667
+ elif (256 / timings_divider) <= escore_averages[1] <= (384 / timings_divider):
9668
  tempo = 'average'
9669
 
9670
+ elif escore_averages[1] > (384 / timings_divider):
9671
  tempo = 'slow'
9672
 
9673
  if escore_averages[2] < 50:
 
9714
 
9715
  description += comp_type + ' composition'
9716
 
9717
+ if comp_type != 'drum track':
9718
 
9719
  if drums_present:
9720
  description += ' with drums'
 
9746
  description += 'is played on a solo ' + instruments[0] + '.'
9747
 
9748
  else:
9749
+ description += 'features ' + NUMERALS[max(0, min(15, len(instruments)-1))] + ' instruments: '
9750
  description += ', '.join(instruments[:-1]) + ' and ' + instruments[-1] + '.'
9751
 
9752
  description += '\n'
9753
 
9754
+ description += 'The song starts with ' + instruments[0] + '.'
9755
+
9756
+ description += '\n'
9757
+
9758
+ description += 'The song main instrument is ' + dominant_instrument + '.'
9759
+
9760
+ description += '\n'
9761
+
9762
+ if drums_present and most_common_drums:
9763
  description += 'The drum track has predominant '
9764
  description += ', '.join(most_common_drums[:-1]) + ' and ' + most_common_drums[-1] + '.'
9765
 
9766
+ description += '\n'
9767
+
9768
  #==============================================================================
9769
 
9770
  return description
9771
 
9772
+ ###################################################################################
9773
+
9774
+ #==================================================================================
9775
+ #
9776
+ # Below constants code is a courtesy of MidiTok
9777
+ #
9778
+ # Retrieved on 12/29/2024
9779
+ #
9780
+ # https://github.com/Natooz/MidiTok/blob/main/src/miditok/constants.py
9781
+ #
9782
+ #==================================================================================
9783
+
9784
+ MIDI_FILES_EXTENSIONS = [".mid", ".midi", ".kar", ".MID", ".MIDI", ".KAR"]
9785
+
9786
+ # The recommended pitches for piano in the GM2 specs are from 21 to 108
9787
+ PIANO_PITCH_RANGE = range(21, 109)
9788
+
9789
+ # Chord params
9790
+ # "chord_unknown" specifies the range of number of notes that can form "unknown" chords
9791
+ # (that do not fit in "chord_maps") to add in tokens.
9792
+ # Known chord maps, with 0 as root note
9793
+ BASIC_CHORDS_MAP = {
9794
+ "min": (0, 3, 7),
9795
+ "maj": (0, 4, 7),
9796
+ "dim": (0, 3, 6),
9797
+ "aug": (0, 4, 8),
9798
+ "sus2": (0, 2, 7),
9799
+ "sus4": (0, 5, 7),
9800
+ "7dom": (0, 4, 7, 10),
9801
+ "7min": (0, 3, 7, 10),
9802
+ "7maj": (0, 4, 7, 11),
9803
+ "7halfdim": (0, 3, 6, 10),
9804
+ "7dim": (0, 3, 6, 9),
9805
+ "7aug": (0, 4, 8, 11),
9806
+ "9maj": (0, 4, 7, 10, 14),
9807
+ "9min": (0, 4, 7, 10, 13),
9808
+ }
9809
+
9810
+ # Drums
9811
+ # Recommended range from the GM2 specs
9812
+ DRUMS_PITCH_RANGE = range(27, 90)
9813
+
9814
+ # Used with chords
9815
+ PITCH_CLASSES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
9816
+
9817
+ # http://newt.phys.unsw.edu.au/jw/notes.html
9818
+ # https://www.midi.org/specifications
9819
+
9820
+ # index i = program i+1 in the GM2 specs (7. Appendix A)
9821
+ # index i = program i as retrieved by packages
9822
+ MIDI_INSTRUMENTS = [
9823
+ # Piano
9824
+ {"name": "Acoustic Grand Piano", "pitch_range": range(21, 109)},
9825
+ {"name": "Bright Acoustic Piano", "pitch_range": range(21, 109)},
9826
+ {"name": "Electric Grand Piano", "pitch_range": range(21, 109)},
9827
+ {"name": "Honky-tonk Piano", "pitch_range": range(21, 109)},
9828
+ {"name": "Electric Piano 1", "pitch_range": range(28, 104)},
9829
+ {"name": "Electric Piano 2", "pitch_range": range(28, 104)},
9830
+ {"name": "Harpsichord", "pitch_range": range(41, 90)},
9831
+ {"name": "Clavi", "pitch_range": range(36, 97)},
9832
+ # Chromatic Percussion
9833
+ {"name": "Celesta", "pitch_range": range(60, 109)},
9834
+ {"name": "Glockenspiel", "pitch_range": range(72, 109)},
9835
+ {"name": "Music Box", "pitch_range": range(60, 85)},
9836
+ {"name": "Vibraphone", "pitch_range": range(53, 90)},
9837
+ {"name": "Marimba", "pitch_range": range(48, 85)},
9838
+ {"name": "Xylophone", "pitch_range": range(65, 97)},
9839
+ {"name": "Tubular Bells", "pitch_range": range(60, 78)},
9840
+ {"name": "Dulcimer", "pitch_range": range(60, 85)},
9841
+ # Organs
9842
+ {"name": "Drawbar Organ", "pitch_range": range(36, 97)},
9843
+ {"name": "Percussive Organ", "pitch_range": range(36, 97)},
9844
+ {"name": "Rock Organ", "pitch_range": range(36, 97)},
9845
+ {"name": "Church Organ", "pitch_range": range(21, 109)},
9846
+ {"name": "Reed Organ", "pitch_range": range(36, 97)},
9847
+ {"name": "Accordion", "pitch_range": range(53, 90)},
9848
+ {"name": "Harmonica", "pitch_range": range(60, 85)},
9849
+ {"name": "Tango Accordion", "pitch_range": range(53, 90)},
9850
+ # Guitars
9851
+ {"name": "Acoustic Guitar (nylon)", "pitch_range": range(40, 85)},
9852
+ {"name": "Acoustic Guitar (steel)", "pitch_range": range(40, 85)},
9853
+ {"name": "Electric Guitar (jazz)", "pitch_range": range(40, 87)},
9854
+ {"name": "Electric Guitar (clean)", "pitch_range": range(40, 87)},
9855
+ {"name": "Electric Guitar (muted)", "pitch_range": range(40, 87)},
9856
+ {"name": "Overdriven Guitar", "pitch_range": range(40, 87)},
9857
+ {"name": "Distortion Guitar", "pitch_range": range(40, 87)},
9858
+ {"name": "Guitar Harmonics", "pitch_range": range(40, 87)},
9859
+ # Bass
9860
+ {"name": "Acoustic Bass", "pitch_range": range(28, 56)},
9861
+ {"name": "Electric Bass (finger)", "pitch_range": range(28, 56)},
9862
+ {"name": "Electric Bass (pick)", "pitch_range": range(28, 56)},
9863
+ {"name": "Fretless Bass", "pitch_range": range(28, 56)},
9864
+ {"name": "Slap Bass 1", "pitch_range": range(28, 56)},
9865
+ {"name": "Slap Bass 2", "pitch_range": range(28, 56)},
9866
+ {"name": "Synth Bass 1", "pitch_range": range(28, 56)},
9867
+ {"name": "Synth Bass 2", "pitch_range": range(28, 56)},
9868
+ # Strings & Orchestral instruments
9869
+ {"name": "Violin", "pitch_range": range(55, 94)},
9870
+ {"name": "Viola", "pitch_range": range(48, 85)},
9871
+ {"name": "Cello", "pitch_range": range(36, 73)},
9872
+ {"name": "Contrabass", "pitch_range": range(28, 56)},
9873
+ {"name": "Tremolo Strings", "pitch_range": range(28, 94)},
9874
+ {"name": "Pizzicato Strings", "pitch_range": range(28, 94)},
9875
+ {"name": "Orchestral Harp", "pitch_range": range(23, 104)},
9876
+ {"name": "Timpani", "pitch_range": range(36, 58)},
9877
+ # Ensembles
9878
+ {"name": "String Ensembles 1", "pitch_range": range(28, 97)},
9879
+ {"name": "String Ensembles 2", "pitch_range": range(28, 97)},
9880
+ {"name": "SynthStrings 1", "pitch_range": range(36, 97)},
9881
+ {"name": "SynthStrings 2", "pitch_range": range(36, 97)},
9882
+ {"name": "Choir Aahs", "pitch_range": range(48, 80)},
9883
+ {"name": "Voice Oohs", "pitch_range": range(48, 80)},
9884
+ {"name": "Synth Voice", "pitch_range": range(48, 85)},
9885
+ {"name": "Orchestra Hit", "pitch_range": range(48, 73)},
9886
+ # Brass
9887
+ {"name": "Trumpet", "pitch_range": range(58, 95)},
9888
+ {"name": "Trombone", "pitch_range": range(34, 76)},
9889
+ {"name": "Tuba", "pitch_range": range(29, 56)},
9890
+ {"name": "Muted Trumpet", "pitch_range": range(58, 83)},
9891
+ {"name": "French Horn", "pitch_range": range(41, 78)},
9892
+ {"name": "Brass Section", "pitch_range": range(36, 97)},
9893
+ {"name": "Synth Brass 1", "pitch_range": range(36, 97)},
9894
+ {"name": "Synth Brass 2", "pitch_range": range(36, 97)},
9895
+ # Reed
9896
+ {"name": "Soprano Sax", "pitch_range": range(54, 88)},
9897
+ {"name": "Alto Sax", "pitch_range": range(49, 81)},
9898
+ {"name": "Tenor Sax", "pitch_range": range(42, 76)},
9899
+ {"name": "Baritone Sax", "pitch_range": range(37, 69)},
9900
+ {"name": "Oboe", "pitch_range": range(58, 92)},
9901
+ {"name": "English Horn", "pitch_range": range(52, 82)},
9902
+ {"name": "Bassoon", "pitch_range": range(34, 73)},
9903
+ {"name": "Clarinet", "pitch_range": range(50, 92)},
9904
+ # Pipe
9905
+ {"name": "Piccolo", "pitch_range": range(74, 109)},
9906
+ {"name": "Flute", "pitch_range": range(60, 97)},
9907
+ {"name": "Recorder", "pitch_range": range(60, 97)},
9908
+ {"name": "Pan Flute", "pitch_range": range(60, 97)},
9909
+ {"name": "Blown Bottle", "pitch_range": range(60, 97)},
9910
+ {"name": "Shakuhachi", "pitch_range": range(55, 85)},
9911
+ {"name": "Whistle", "pitch_range": range(60, 97)},
9912
+ {"name": "Ocarina", "pitch_range": range(60, 85)},
9913
+ # Synth Lead
9914
+ {"name": "Lead 1 (square)", "pitch_range": range(21, 109)},
9915
+ {"name": "Lead 2 (sawtooth)", "pitch_range": range(21, 109)},
9916
+ {"name": "Lead 3 (calliope)", "pitch_range": range(36, 97)},
9917
+ {"name": "Lead 4 (chiff)", "pitch_range": range(36, 97)},
9918
+ {"name": "Lead 5 (charang)", "pitch_range": range(36, 97)},
9919
+ {"name": "Lead 6 (voice)", "pitch_range": range(36, 97)},
9920
+ {"name": "Lead 7 (fifths)", "pitch_range": range(36, 97)},
9921
+ {"name": "Lead 8 (bass + lead)", "pitch_range": range(21, 109)},
9922
+ # Synth Pad
9923
+ {"name": "Pad 1 (new age)", "pitch_range": range(36, 97)},
9924
+ {"name": "Pad 2 (warm)", "pitch_range": range(36, 97)},
9925
+ {"name": "Pad 3 (polysynth)", "pitch_range": range(36, 97)},
9926
+ {"name": "Pad 4 (choir)", "pitch_range": range(36, 97)},
9927
+ {"name": "Pad 5 (bowed)", "pitch_range": range(36, 97)},
9928
+ {"name": "Pad 6 (metallic)", "pitch_range": range(36, 97)},
9929
+ {"name": "Pad 7 (halo)", "pitch_range": range(36, 97)},
9930
+ {"name": "Pad 8 (sweep)", "pitch_range": range(36, 97)},
9931
+ # Synth SFX
9932
+ {"name": "FX 1 (rain)", "pitch_range": range(36, 97)},
9933
+ {"name": "FX 2 (soundtrack)", "pitch_range": range(36, 97)},
9934
+ {"name": "FX 3 (crystal)", "pitch_range": range(36, 97)},
9935
+ {"name": "FX 4 (atmosphere)", "pitch_range": range(36, 97)},
9936
+ {"name": "FX 5 (brightness)", "pitch_range": range(36, 97)},
9937
+ {"name": "FX 6 (goblins)", "pitch_range": range(36, 97)},
9938
+ {"name": "FX 7 (echoes)", "pitch_range": range(36, 97)},
9939
+ {"name": "FX 8 (sci-fi)", "pitch_range": range(36, 97)},
9940
+ # Ethnic Misc.
9941
+ {"name": "Sitar", "pitch_range": range(48, 78)},
9942
+ {"name": "Banjo", "pitch_range": range(48, 85)},
9943
+ {"name": "Shamisen", "pitch_range": range(50, 80)},
9944
+ {"name": "Koto", "pitch_range": range(55, 85)},
9945
+ {"name": "Kalimba", "pitch_range": range(48, 80)},
9946
+ {"name": "Bag pipe", "pitch_range": range(36, 78)},
9947
+ {"name": "Fiddle", "pitch_range": range(55, 97)},
9948
+ {"name": "Shanai", "pitch_range": range(48, 73)},
9949
+ # Percussive
9950
+ {"name": "Tinkle Bell", "pitch_range": range(72, 85)},
9951
+ {"name": "Agogo", "pitch_range": range(60, 73)},
9952
+ {"name": "Steel Drums", "pitch_range": range(52, 77)},
9953
+ {"name": "Woodblock", "pitch_range": range(128)},
9954
+ {"name": "Taiko Drum", "pitch_range": range(128)},
9955
+ {"name": "Melodic Tom", "pitch_range": range(128)},
9956
+ {"name": "Synth Drum", "pitch_range": range(128)},
9957
+ {"name": "Reverse Cymbal", "pitch_range": range(128)},
9958
+ # SFX
9959
+ {"name": "Guitar Fret Noise, Guitar Cutting Noise", "pitch_range": range(128)},
9960
+ {"name": "Breath Noise, Flute Key Click", "pitch_range": range(128)},
9961
+ {
9962
+ "name": "Seashore, Rain, Thunder, Wind, Stream, Bubbles",
9963
+ "pitch_range": range(128),
9964
+ },
9965
+ {"name": "Bird Tweet, Dog, Horse Gallop", "pitch_range": range(128)},
9966
+ {
9967
+ "name": "Telephone Ring, Door Creaking, Door, Scratch, Wind Chime",
9968
+ "pitch_range": range(128),
9969
+ },
9970
+ {"name": "Helicopter, Car Sounds", "pitch_range": range(128)},
9971
+ {
9972
+ "name": "Applause, Laughing, Screaming, Punch, Heart Beat, Footstep",
9973
+ "pitch_range": range(128),
9974
+ },
9975
+ {"name": "Gunshot, Machine Gun, Lasergun, Explosion", "pitch_range": range(128)},
9976
+ ]
9977
+
9978
+ INSTRUMENTS_CLASSES = [
9979
+ {"name": "Piano", "program_range": range(8)}, # 0
9980
+ {"name": "Chromatic Percussion", "program_range": range(8, 16)},
9981
+ {"name": "Organ", "program_range": range(16, 24)},
9982
+ {"name": "Guitar", "program_range": range(24, 32)},
9983
+ {"name": "Bass", "program_range": range(32, 40)},
9984
+ {"name": "Strings", "program_range": range(40, 48)}, # 5
9985
+ {"name": "Ensemble", "program_range": range(48, 56)},
9986
+ {"name": "Brass", "program_range": range(56, 64)},
9987
+ {"name": "Reed", "program_range": range(64, 72)},
9988
+ {"name": "Pipe", "program_range": range(72, 80)},
9989
+ {"name": "Synth Lead", "program_range": range(80, 88)}, # 10
9990
+ {"name": "Synth Pad", "program_range": range(88, 96)},
9991
+ {"name": "Synth Effects", "program_range": range(96, 104)},
9992
+ {"name": "Ethnic", "program_range": range(104, 112)},
9993
+ {"name": "Percussive", "program_range": range(112, 120)},
9994
+ {"name": "Sound Effects", "program_range": range(120, 128)}, # 15
9995
+ {"name": "Drums", "program_range": range(-1, 0)},
9996
+ ]
9997
+
9998
+ # To easily get the class index of any instrument program
9999
+ CLASS_OF_INST = [
10000
+ i
10001
+ for i, inst_class in enumerate(INSTRUMENTS_CLASSES)
10002
+ for _ in inst_class["program_range"]
10003
+ ]
10004
+
10005
+ # index i = program i+1 in the GM2 specs (8. Appendix B)
10006
+ # index i = program i retrieved by packages
10007
+ DRUMS_SETS = {
10008
+ 0: "Standard",
10009
+ 8: "Room",
10010
+ 16: "Power",
10011
+ 24: "Electronic",
10012
+ 25: "Analog",
10013
+ 32: "Jazz",
10014
+ 40: "Brush",
10015
+ 48: "Orchestra",
10016
+ 56: "SFX",
10017
+ }
10018
+
10019
+ # Control changes list (without specifications):
10020
+ # https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
10021
+ # Undefined and general control changes are not considered here
10022
+ # All these attributes can take values from 0 to 127, with some of them being on/off
10023
+ CONTROL_CHANGES = {
10024
+ # MSB
10025
+ 0: "Bank Select",
10026
+ 1: "Modulation Depth",
10027
+ 2: "Breath Controller",
10028
+ 4: "Foot Controller",
10029
+ 5: "Portamento Time",
10030
+ 6: "Data Entry",
10031
+ 7: "Channel Volume",
10032
+ 8: "Balance",
10033
+ 10: "Pan",
10034
+ 11: "Expression Controller",
10035
+ # LSB
10036
+ 32: "Bank Select",
10037
+ 33: "Modulation Depth",
10038
+ 34: "Breath Controller",
10039
+ 36: "Foot Controller",
10040
+ 37: "Portamento Time",
10041
+ 38: "Data Entry",
10042
+ 39: "Channel Volume",
10043
+ 40: "Balance",
10044
+ 42: "Pan",
10045
+ 43: "Expression Controller",
10046
+ # On / Off control changes, ≤63 off, ≥64 on
10047
+ 64: "Damper Pedal",
10048
+ 65: "Portamento",
10049
+ 66: "Sostenuto",
10050
+ 67: "Soft Pedal",
10051
+ 68: "Legato Footswitch",
10052
+ 69: "Hold 2",
10053
+ # Continuous controls
10054
+ 70: "Sound Variation",
10055
+ 71: "Timbre/Harmonic Intensity",
10056
+ 72: "Release Time",
10057
+ 73: "Attack Time",
10058
+ 74: "Brightness",
10059
+ 75: "Decay Time",
10060
+ 76: "Vibrato Rate",
10061
+ 77: "Vibrato Depth",
10062
+ 78: "Vibrato Delay",
10063
+ 84: "Portamento Control",
10064
+ 88: "High Resolution Velocity Prefix",
10065
+ # Effects depths
10066
+ 91: "Reverb Depth",
10067
+ 92: "Tremolo Depth",
10068
+ 93: "Chorus Depth",
10069
+ 94: "Celeste Depth",
10070
+ 95: "Phaser Depth",
10071
+ # Registered parameters numbers
10072
+ 96: "Data Increment",
10073
+ 97: "Data Decrement",
10074
+ # 98: 'Non-Registered Parameter Number (NRPN) - LSB',
10075
+ # 99: 'Non-Registered Parameter Number (NRPN) - MSB',
10076
+ 100: "Registered Parameter Number (RPN) - LSB",
10077
+ 101: "Registered Parameter Number (RPN) - MSB",
10078
+ # Channel mode controls
10079
+ 120: "All Sound Off",
10080
+ 121: "Reset All Controllers",
10081
+ 122: "Local Control On/Off",
10082
+ 123: "All Notes Off",
10083
+ 124: "Omni Mode Off", # + all notes off
10084
+ 125: "Omni Mode On", # + all notes off
10085
+ 126: "Mono Mode On", # + poly off, + all notes off
10086
+ 127: "Poly Mode On", # + mono off, +all notes off
10087
+ }
10088
+
10089
+ ###################################################################################
10090
+
10091
+ def patches_onset_times(escore_notes, times_idx=1, patches_idx=6):
10092
+
10093
+ patches = [e[patches_idx] for e in escore_notes]
10094
+
10095
+ patches_oset = ordered_set(patches)
10096
+
10097
+ patches_onset_times = []
10098
+
10099
+ for p in patches_oset:
10100
+ for e in escore_notes:
10101
+ if e[patches_idx] == p:
10102
+ patches_onset_times.append([p, e[times_idx]])
10103
+ break
10104
+
10105
+ return patches_onset_times
10106
+
10107
+ ###################################################################################
10108
+
10109
+ def count_escore_notes_patches(escore_notes, patches_idx=6):
10110
+
10111
+ patches = [e[patches_idx] for e in escore_notes]
10112
+
10113
+ return Counter(patches).most_common()
10114
+
10115
+ ###################################################################################
10116
+
10117
+ def escore_notes_monoponic_melodies(escore_notes,
10118
+ bad_notes_ratio=0.0,
10119
+ times_idx=1,
10120
+ patches_idx=6
10121
+ ):
10122
+
10123
+ patches = escore_notes_patches(escore_notes, patches_index=patches_idx)
10124
+
10125
+ monophonic_melodies = []
10126
+
10127
+ for p in patches:
10128
+ patch_score = [e for e in escore_notes if e[patches_idx] == p]
10129
+
10130
+ ps_times = [e[times_idx] for e in patch_score]
10131
+
10132
+ if len(ps_times) <= len(set(ps_times)) * (1+bad_notes_ratio):
10133
+ monophonic_melodies.append([p, len(patch_score)])
10134
+
10135
+ return monophonic_melodies
10136
+
10137
+ ###################################################################################
10138
+
10139
+ from itertools import groupby
10140
+ from operator import itemgetter
10141
+
10142
+ def group_by_threshold(data, threshold, groupby_idx):
10143
+
10144
+ data.sort(key=itemgetter(groupby_idx))
10145
+
10146
+ grouped_data = []
10147
+ cluster = []
10148
+
10149
+ for i, item in enumerate(data):
10150
+ if not cluster:
10151
+ cluster.append(item)
10152
+ elif abs(item[groupby_idx] - cluster[-1][groupby_idx]) <= threshold:
10153
+ cluster.append(item)
10154
+ else:
10155
+ grouped_data.append(cluster)
10156
+ cluster = [item]
10157
+
10158
+ if cluster:
10159
+ grouped_data.append(cluster)
10160
+
10161
+ return grouped_data
10162
+
10163
+ ###################################################################################
10164
+
10165
+ def split_escore_notes_by_time(escore_notes, time_threshold=256):
10166
+
10167
+ dscore = delta_score_notes(escore_notes, timings_clip_value=time_threshold-1)
10168
+
10169
+ score_chunks = []
10170
+
10171
+ ctime = 0
10172
+ pchunk_idx = 0
10173
+
10174
+ for i, e in enumerate(dscore):
10175
+
10176
+ ctime += e[1]
10177
+
10178
+ if ctime >= time_threshold:
10179
+ score_chunks.append(escore_notes[pchunk_idx:i])
10180
+ pchunk_idx = i
10181
+ ctime = 0
10182
+
10183
+ return score_chunks
10184
+
10185
+ ###################################################################################
10186
+
10187
+ def escore_notes_grouped_patches(escore_notes, time_threshold=256):
10188
+
10189
+ split_score_chunks = split_escore_notes_by_time(escore_notes,
10190
+ time_threshold=time_threshold
10191
+ )
10192
+
10193
+ chunks_patches = []
10194
+
10195
+ for s in split_score_chunks:
10196
+ chunks_patches.append(escore_notes_patches(s))
10197
+
10198
+ return chunks_patches
10199
+
10200
+ ###################################################################################
10201
+
10202
+ def computeLPSArray(pattern, M, lps):
10203
+ length = 0
10204
+ i = 1
10205
+
10206
+ lps[0] = 0
10207
+
10208
+ while i < M:
10209
+ if pattern[i] == pattern[length]:
10210
+ length += 1
10211
+ lps[i] = length
10212
+ i += 1
10213
+ else:
10214
+ if length != 0:
10215
+ length = lps[length-1]
10216
+ else:
10217
+ lps[i] = 0
10218
+ i += 1
10219
+
10220
+ ###################################################################################
10221
+
10222
+ def find_pattern_idxs(sub_pattern, pattern):
10223
+
10224
+ lst = pattern
10225
+ pattern = sub_pattern
10226
+
10227
+ M = len(pattern)
10228
+ N = len(lst)
10229
+
10230
+ lps = [0] * M
10231
+ j = 0 # index for pattern[]
10232
+
10233
+ computeLPSArray(pattern, M, lps)
10234
+
10235
+ i = 0 # index for lst[]
10236
+ indexes = []
10237
+
10238
+ while i < N:
10239
+ if pattern[j] == lst[i]:
10240
+ i += 1
10241
+ j += 1
10242
+
10243
+ if j == M:
10244
+ end_index = i - 1
10245
+ start_index = end_index - M + 1
10246
+ indexes.append((start_index, end_index))
10247
+ j = lps[j-1]
10248
+ elif i < N and pattern[j] != lst[i]:
10249
+ if j != 0:
10250
+ j = lps[j-1]
10251
+ else:
10252
+ i += 1
10253
+
10254
+ return indexes
10255
+
10256
+ ###################################################################################
10257
+
10258
+ def escore_notes_patch_lrno_patterns(escore_notes,
10259
+ patch=0,
10260
+ zero_score_timings=False,
10261
+ pitches_idx=4,
10262
+ patches_idx=6
10263
+ ):
10264
+
10265
+ patch_escore = [e for e in escore_notes if e[patches_idx] == patch]
10266
+
10267
+ if patch_escore:
10268
+
10269
+ patch_cscore = chordify_score([1000, patch_escore])
10270
+
10271
+ patch_tscore = []
10272
+
10273
+ for c in patch_cscore:
10274
+
10275
+ tones_chord = sorted(set([p[pitches_idx] % 12 for p in c]))
10276
+
10277
+ if tones_chord not in ALL_CHORDS_SORTED:
10278
+ tnoes_chord = check_and_fix_tones_chord(tones_chord)
10279
+
10280
+ patch_tscore.append(ALL_CHORDS_SORTED.index(tones_chord))
10281
+
10282
+ pattern = find_lrno_pattern_fast(patch_tscore)
10283
+
10284
+ patterns_idxs = find_pattern_idxs(pattern, patch_tscore)
10285
+
10286
+ patch_lrno_scores = []
10287
+
10288
+ for idxs in patterns_idxs:
10289
+
10290
+ score = patch_escore[idxs[0]:idxs[1]]
10291
+
10292
+ if zero_score_timings:
10293
+ score = recalculate_score_timings(score)
10294
+
10295
+ patch_lrno_scores.append(score)
10296
+
10297
+ return patch_lrno_scores
10298
+
10299
+ else:
10300
+ return []
10301
+
10302
+ ###################################################################################
10303
+
10304
+ ALL_BASE_CHORDS_SORTED = [[0], [0, 2], [0, 2, 4], [0, 2, 4, 6], [0, 2, 4, 6, 8], [0, 2, 4, 6, 8, 10],
10305
+ [0, 2, 4, 6, 9], [0, 2, 4, 6, 10], [0, 2, 4, 7], [0, 2, 4, 7, 9],
10306
+ [0, 2, 4, 7, 10], [0, 2, 4, 8], [0, 2, 4, 8, 10], [0, 2, 4, 9], [0, 2, 4, 10],
10307
+ [0, 2, 5], [0, 2, 5, 7], [0, 2, 5, 7, 9], [0, 2, 5, 7, 10], [0, 2, 5, 8],
10308
+ [0, 2, 5, 8, 10], [0, 2, 5, 9], [0, 2, 5, 10], [0, 2, 6], [0, 2, 6, 8],
10309
+ [0, 2, 6, 8, 10], [0, 2, 6, 9], [0, 2, 6, 10], [0, 2, 7], [0, 2, 7, 9],
10310
+ [0, 2, 7, 10], [0, 2, 8], [0, 2, 8, 10], [0, 2, 9], [0, 2, 10], [0, 3],
10311
+ [0, 3, 5], [0, 3, 5, 7], [0, 3, 5, 7, 9], [0, 3, 5, 7, 10], [0, 3, 5, 8],
10312
+ [0, 3, 5, 8, 10], [0, 3, 5, 9], [0, 3, 5, 10], [0, 3, 6], [0, 3, 6, 8],
10313
+ [0, 3, 6, 8, 10], [0, 3, 6, 9], [0, 3, 6, 10], [0, 3, 7], [0, 3, 7, 9],
10314
+ [0, 3, 7, 10], [0, 3, 8], [0, 3, 8, 10], [0, 3, 9], [0, 3, 10], [0, 4],
10315
+ [0, 4, 6], [0, 4, 6, 8], [0, 4, 6, 8, 10], [0, 4, 6, 9], [0, 4, 6, 10],
10316
+ [0, 4, 7], [0, 4, 7, 9], [0, 4, 7, 10], [0, 4, 8], [0, 4, 8, 10], [0, 4, 9],
10317
+ [0, 4, 10], [0, 5], [0, 5, 7], [0, 5, 7, 9], [0, 5, 7, 10], [0, 5, 8],
10318
+ [0, 5, 8, 10], [0, 5, 9], [0, 5, 10], [0, 6], [0, 6, 8], [0, 6, 8, 10],
10319
+ [0, 6, 9], [0, 6, 10], [0, 7], [0, 7, 9], [0, 7, 10], [0, 8], [0, 8, 10],
10320
+ [0, 9], [0, 10]]
10321
+
10322
  ###################################################################################
10323
  #
10324
  # This is the end of the TMIDI X Python module