asigalov61
commited on
Commit
•
140ba03
1
Parent(s):
8d69657
Upload TMIDIX.py
Browse files
TMIDIX.py
CHANGED
@@ -56,6 +56,8 @@ VersionDate = '20201120'
|
|
56 |
|
57 |
_previous_warning = '' # 5.4
|
58 |
_previous_times = 0 # 5.4
|
|
|
|
|
59 |
#------------------------------- Encoding stuff --------------------------
|
60 |
|
61 |
def opus2midi(opus=[], text_encoding='ISO-8859-1'):
|
@@ -845,10 +847,11 @@ def _unshift_ber_int(ba):
|
|
845 |
r'''Given a bytearray, returns a tuple of (the ber-integer at the
|
846 |
start, and the remainder of the bytearray).
|
847 |
'''
|
848 |
-
if not len(ba):
|
849 |
_warn('_unshift_ber_int: no integer found')
|
850 |
return ((0, b""))
|
851 |
-
byte = ba
|
|
|
852 |
integer = 0
|
853 |
while True:
|
854 |
integer += (byte & 0x7F)
|
@@ -857,13 +860,17 @@ start, and the remainder of the bytearray).
|
|
857 |
if not len(ba):
|
858 |
_warn('_unshift_ber_int: no end-of-integer found')
|
859 |
return ((0, ba))
|
860 |
-
byte = ba
|
|
|
861 |
integer <<= 7
|
862 |
|
|
|
863 |
def _clean_up_warnings(): # 5.4
|
864 |
# Call this before returning from any publicly callable function
|
865 |
# whenever there's a possibility that a warning might have been printed
|
866 |
# by the function, or by any private functions it might have called.
|
|
|
|
|
867 |
global _previous_times
|
868 |
global _previous_warning
|
869 |
if _previous_times > 1:
|
@@ -876,27 +883,32 @@ def _clean_up_warnings(): # 5.4
|
|
876 |
_previous_times = 0
|
877 |
_previous_warning = ''
|
878 |
|
|
|
879 |
def _warn(s=''):
|
|
|
|
|
880 |
global _previous_times
|
881 |
global _previous_warning
|
882 |
if s == _previous_warning: # 5.4
|
883 |
_previous_times = _previous_times + 1
|
884 |
else:
|
885 |
_clean_up_warnings()
|
886 |
-
sys.stderr.write(str(s)+"\n")
|
887 |
_previous_warning = s
|
888 |
|
|
|
889 |
def _some_text_event(which_kind=0x01, text=b'some_text', text_encoding='ISO-8859-1'):
|
890 |
-
if str(type(text)).find("'str'") >= 0:
|
891 |
data = bytes(text, encoding=text_encoding)
|
892 |
else:
|
893 |
data = bytes(text)
|
894 |
-
return b'\xFF'+bytes((which_kind,))+_ber_compressed_int(len(data))+data
|
|
|
895 |
|
896 |
def _consistentise_ticks(scores): # 3.6
|
897 |
# used by mix_scores, merge_scores, concatenate_scores
|
898 |
if len(scores) == 1:
|
899 |
-
|
900 |
are_consistent = True
|
901 |
ticks = scores[0][0]
|
902 |
iscore = 1
|
@@ -917,9 +929,8 @@ def _consistentise_ticks(scores): # 3.6
|
|
917 |
|
918 |
|
919 |
###########################################################################
|
920 |
-
|
921 |
def _decode(trackdata=b'', exclude=None, include=None,
|
922 |
-
|
923 |
r'''Decodes MIDI track data into an opus-style list of events.
|
924 |
The options:
|
925 |
'exclude' is a list of event types which will be ignored SHOULD BE A SET
|
@@ -939,24 +950,24 @@ The options:
|
|
939 |
exclude = set(exclude)
|
940 |
|
941 |
# Pointer = 0; not used here; we eat through the bytearray instead.
|
942 |
-
event_code = -1;
|
943 |
event_count = 0;
|
944 |
events = []
|
945 |
|
946 |
-
while(len(trackdata)):
|
947 |
# loop while there's anything to analyze ...
|
948 |
-
eot = False
|
949 |
event_count += 1
|
950 |
|
951 |
E = []
|
952 |
# E for events - we'll feed it to the event registrar at the end.
|
953 |
|
954 |
# Slice off the delta time code, and analyze it
|
955 |
-
[time,
|
956 |
|
957 |
# Now let's see what we can make of the command
|
958 |
-
first_byte = trackdata
|
959 |
-
|
960 |
if (first_byte < 0xF0): # It's a MIDI event
|
961 |
if (first_byte & 0x80):
|
962 |
event_code = first_byte
|
@@ -970,17 +981,19 @@ The options:
|
|
970 |
command = event_code & 0xF0
|
971 |
channel = event_code & 0x0F
|
972 |
|
973 |
-
if (command == 0xF6): #
|
974 |
pass
|
975 |
-
elif (command == 0xC0 or command == 0xD0): #
|
976 |
-
parameter = trackdata
|
977 |
-
|
978 |
-
|
|
|
|
|
979 |
|
980 |
#################################################################
|
981 |
# MIDI events
|
982 |
|
983 |
-
if (command
|
984 |
if 'note_off' in exclude:
|
985 |
continue
|
986 |
E = ['note_off', time, channel, parameter[0], parameter[1]]
|
@@ -991,11 +1004,11 @@ The options:
|
|
991 |
elif (command == 0xA0):
|
992 |
if 'key_after_touch' in exclude:
|
993 |
continue
|
994 |
-
E = ['key_after_touch',time,channel,parameter[0],parameter[1]]
|
995 |
elif (command == 0xB0):
|
996 |
if 'control_change' in exclude:
|
997 |
continue
|
998 |
-
E = ['control_change',time,channel,parameter[0],parameter[1]]
|
999 |
elif (command == 0xC0):
|
1000 |
if 'patch_change' in exclude:
|
1001 |
continue
|
@@ -1008,93 +1021,94 @@ The options:
|
|
1008 |
if 'pitch_wheel_change' in exclude:
|
1009 |
continue
|
1010 |
E = ['pitch_wheel_change', time, channel,
|
1011 |
-
|
1012 |
else:
|
1013 |
-
_warn("Shouldn't get here; command="+hex(command))
|
1014 |
|
1015 |
elif (first_byte == 0xFF): # It's a Meta-Event! ##################
|
1016 |
-
#[command, length, remainder] =
|
1017 |
# unpack("xCwa*", substr(trackdata, $Pointer, 6));
|
1018 |
-
#Pointer += 6 - len(remainder);
|
1019 |
# # Move past JUST the length-encoded.
|
1020 |
-
command = trackdata
|
|
|
1021 |
[length, trackdata] = _unshift_ber_int(trackdata)
|
1022 |
-
if (command
|
1023 |
-
|
1024 |
-
|
1025 |
-
|
1026 |
-
|
1027 |
-
|
1028 |
-
|
1029 |
-
elif command >= 0x01 and command <= 0x0f:
|
1030 |
# 6.2 take it in bytes; let the user get the right encoding.
|
1031 |
# text_str = trackdata[0:length].decode('ascii','ignore')
|
1032 |
# text_str = trackdata[0:length].decode('ISO-8859-1')
|
1033 |
# 6.4 take it in bytes; let the user get the right encoding.
|
1034 |
-
text_data = bytes(trackdata[0:length])
|
1035 |
# Defined text events
|
1036 |
if (command == 0x01):
|
1037 |
-
|
1038 |
elif (command == 0x02):
|
1039 |
-
|
1040 |
elif (command == 0x03):
|
1041 |
-
|
1042 |
elif (command == 0x04):
|
1043 |
-
|
1044 |
elif (command == 0x05):
|
1045 |
-
|
1046 |
elif (command == 0x06):
|
1047 |
-
|
1048 |
elif (command == 0x07):
|
1049 |
-
|
1050 |
# Reserved but apparently unassigned text events
|
1051 |
elif (command == 0x08):
|
1052 |
-
|
1053 |
elif (command == 0x09):
|
1054 |
-
|
1055 |
elif (command == 0x0a):
|
1056 |
-
|
1057 |
elif (command == 0x0b):
|
1058 |
-
|
1059 |
elif (command == 0x0c):
|
1060 |
-
|
1061 |
elif (command == 0x0d):
|
1062 |
-
|
1063 |
elif (command == 0x0e):
|
1064 |
-
|
1065 |
elif (command == 0x0f):
|
1066 |
-
|
1067 |
|
1068 |
# Now the sticky events -------------------------------------
|
1069 |
elif (command == 0x2F):
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
elif (command == 0x51):
|
1074 |
-
|
1075 |
-
|
1076 |
-
|
1077 |
-
|
1078 |
elif (command == 0x54):
|
1079 |
-
|
1080 |
-
|
1081 |
-
|
1082 |
elif (command == 0x58):
|
1083 |
-
|
1084 |
-
|
1085 |
-
|
1086 |
elif (command == 0x59):
|
1087 |
-
|
1088 |
-
|
1089 |
-
|
1090 |
-
elif (command == 0x7F):
|
1091 |
-
|
1092 |
else:
|
1093 |
-
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
|
1098 |
|
1099 |
# Pointer += length; # Now move Pointer
|
1100 |
trackdata = trackdata[length:]
|
@@ -1111,7 +1125,7 @@ The options:
|
|
1111 |
# is omitted if this is a non-final block in a multiblock sysex;
|
1112 |
# but the F7 (if there) is counted in the message's declared
|
1113 |
# length, so we don't have to think about it anyway.)
|
1114 |
-
#command = trackdata.pop(0)
|
1115 |
[length, trackdata] = _unshift_ber_int(trackdata)
|
1116 |
if first_byte == 0xF0:
|
1117 |
# 20091008 added ISO-8859-1 to get an 8-bit str
|
@@ -1135,32 +1149,32 @@ The options:
|
|
1135 |
# from the MIDI file spec. So, I'm going to assume that
|
1136 |
# they CAN, in practice, occur. I don't know whether it's
|
1137 |
# proper for you to actually emit these into a MIDI file.
|
1138 |
-
|
1139 |
-
elif (first_byte == 0xF2):
|
1140 |
# <song position msg> ::= F2 <data pair>
|
1141 |
E = ['song_position', time, _read_14_bit(trackdata[:2])]
|
1142 |
trackdata = trackdata[2:]
|
1143 |
|
1144 |
-
elif (first_byte == 0xF3):
|
1145 |
# E = ['song_select', time, struct.unpack('>B',trackdata.pop(0))[0]]
|
1146 |
E = ['song_select', time, trackdata[0]]
|
1147 |
trackdata = trackdata[1:]
|
1148 |
# DTime, Thing (what?! song number? whatever ...)
|
1149 |
|
1150 |
-
elif (first_byte == 0xF6):
|
1151 |
E = ['tune_request', time]
|
1152 |
# What would a tune request be doing in a MIDI /file/?
|
1153 |
|
1154 |
-
|
1155 |
-
|
1156 |
-
|
1157 |
-
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
1163 |
-
|
1164 |
|
1165 |
r'''
|
1166 |
elif (first_byte > 0xF0) { # Some unknown kinda F-series event ####
|
@@ -1175,31 +1189,30 @@ The options:
|
|
1175 |
elif first_byte > 0xF0: # Some unknown F-series event
|
1176 |
# Here we only produce a one-byte piece of raw data.
|
1177 |
# E = ['raw_data', time, bytest(trackdata[0])] # 6.4
|
1178 |
-
E = ['raw_data', time, trackdata[0]]
|
1179 |
trackdata = trackdata[1:]
|
1180 |
else: # Fallthru.
|
1181 |
-
_warn("Aborting track. Command-byte first_byte="+hex(first_byte))
|
1182 |
break
|
1183 |
# End of the big if-group
|
1184 |
|
1185 |
-
|
1186 |
######################################################################
|
1187 |
# THE EVENT REGISTRAR...
|
1188 |
-
if E and
|
1189 |
# This is the code for exceptional handling of the EOT event.
|
1190 |
eot = True
|
1191 |
if not no_eot_magic:
|
1192 |
if E[1] > 0: # a null text-event to carry the delta-time
|
1193 |
E = ['text_event', E[1], '']
|
1194 |
else:
|
1195 |
-
E = []
|
1196 |
-
|
1197 |
if E and not (E[0] in exclude):
|
1198 |
-
#if ( $exclusive_event_callback ):
|
1199 |
# &{ $exclusive_event_callback }( @E );
|
1200 |
-
#else:
|
1201 |
# &{ $event_callback }( @E ) if $event_callback;
|
1202 |
-
|
1203 |
if eot:
|
1204 |
break
|
1205 |
|
@@ -1819,6 +1832,7 @@ def plot_ms_SONG(ms_song,
|
|
1819 |
plt.close()
|
1820 |
|
1821 |
if return_plt:
|
|
|
1822 |
return fig
|
1823 |
|
1824 |
plt.show()
|
@@ -4595,12 +4609,18 @@ def ascii_text_words_counter(ascii_text):
|
|
4595 |
|
4596 |
###################################################################################
|
4597 |
|
4598 |
-
def check_and_fix_tones_chord(tones_chord):
|
4599 |
|
4600 |
tones_chord_combs = [list(comb) for i in range(len(tones_chord), 0, -1) for comb in combinations(tones_chord, i)]
|
4601 |
|
|
|
|
|
|
|
|
|
|
|
|
|
4602 |
for c in tones_chord_combs:
|
4603 |
-
if c in
|
4604 |
checked_tones_chord = c
|
4605 |
break
|
4606 |
|
@@ -4613,12 +4633,18 @@ def find_closest_tone(tones, tone):
|
|
4613 |
|
4614 |
###################################################################################
|
4615 |
|
4616 |
-
def advanced_check_and_fix_tones_chord(tones_chord, high_pitch=0):
|
4617 |
|
4618 |
tones_chord_combs = [list(comb) for i in range(len(tones_chord), 0, -1) for comb in combinations(tones_chord, i)]
|
4619 |
|
|
|
|
|
|
|
|
|
|
|
|
|
4620 |
for c in tones_chord_combs:
|
4621 |
-
if c in
|
4622 |
tchord = c
|
4623 |
|
4624 |
if 0 < high_pitch < 128 and len(tchord) == 1:
|
@@ -4664,7 +4690,7 @@ def augment_enhanced_score_notes(enhanced_score_notes,
|
|
4664 |
pitch_shift=0,
|
4665 |
ceil_timings=False,
|
4666 |
round_timings=False,
|
4667 |
-
legacy_timings=
|
4668 |
):
|
4669 |
|
4670 |
esn = copy.deepcopy(enhanced_score_notes)
|
@@ -7857,19 +7883,21 @@ def solo_piano_escore_notes(escore_notes,
|
|
7857 |
chord = []
|
7858 |
|
7859 |
for cc in c:
|
7860 |
-
if cc[pitches_index] not in seen:
|
7861 |
|
7862 |
-
|
|
|
|
|
7863 |
cc[channels_index] = 0
|
7864 |
cc[patches_index] = 0
|
7865 |
-
|
7866 |
chord.append(cc)
|
7867 |
seen.append(cc[pitches_index])
|
7868 |
-
|
7869 |
-
|
7870 |
-
|
|
|
7871 |
chord.append(cc)
|
7872 |
-
seen.append(cc[pitches_index])
|
7873 |
|
7874 |
sp_escore_notes.append(chord)
|
7875 |
|
@@ -9045,7 +9073,190 @@ def escore_notes_lrno_pattern_fast(escore_notes,
|
|
9045 |
else:
|
9046 |
return None
|
9047 |
|
9048 |
-
###################################################################################
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9049 |
#
|
9050 |
# This is the end of the TMIDI X Python module
|
9051 |
#
|
|
|
56 |
|
57 |
_previous_warning = '' # 5.4
|
58 |
_previous_times = 0 # 5.4
|
59 |
+
_no_warning = False
|
60 |
+
|
61 |
#------------------------------- Encoding stuff --------------------------
|
62 |
|
63 |
def opus2midi(opus=[], text_encoding='ISO-8859-1'):
|
|
|
847 |
r'''Given a bytearray, returns a tuple of (the ber-integer at the
|
848 |
start, and the remainder of the bytearray).
|
849 |
'''
|
850 |
+
if not len(ba): # 6.7
|
851 |
_warn('_unshift_ber_int: no integer found')
|
852 |
return ((0, b""))
|
853 |
+
byte = ba[0]
|
854 |
+
ba = ba[1:]
|
855 |
integer = 0
|
856 |
while True:
|
857 |
integer += (byte & 0x7F)
|
|
|
860 |
if not len(ba):
|
861 |
_warn('_unshift_ber_int: no end-of-integer found')
|
862 |
return ((0, ba))
|
863 |
+
byte = ba[0]
|
864 |
+
ba = ba[1:]
|
865 |
integer <<= 7
|
866 |
|
867 |
+
|
868 |
def _clean_up_warnings(): # 5.4
|
869 |
# Call this before returning from any publicly callable function
|
870 |
# whenever there's a possibility that a warning might have been printed
|
871 |
# by the function, or by any private functions it might have called.
|
872 |
+
if _no_warning:
|
873 |
+
return
|
874 |
global _previous_times
|
875 |
global _previous_warning
|
876 |
if _previous_times > 1:
|
|
|
883 |
_previous_times = 0
|
884 |
_previous_warning = ''
|
885 |
|
886 |
+
|
887 |
def _warn(s=''):
|
888 |
+
if _no_warning:
|
889 |
+
return
|
890 |
global _previous_times
|
891 |
global _previous_warning
|
892 |
if s == _previous_warning: # 5.4
|
893 |
_previous_times = _previous_times + 1
|
894 |
else:
|
895 |
_clean_up_warnings()
|
896 |
+
sys.stderr.write(str(s) + "\n")
|
897 |
_previous_warning = s
|
898 |
|
899 |
+
|
900 |
def _some_text_event(which_kind=0x01, text=b'some_text', text_encoding='ISO-8859-1'):
|
901 |
+
if str(type(text)).find("'str'") >= 0: # 6.4 test for back-compatibility
|
902 |
data = bytes(text, encoding=text_encoding)
|
903 |
else:
|
904 |
data = bytes(text)
|
905 |
+
return b'\xFF' + bytes((which_kind,)) + _ber_compressed_int(len(data)) + data
|
906 |
+
|
907 |
|
908 |
def _consistentise_ticks(scores): # 3.6
|
909 |
# used by mix_scores, merge_scores, concatenate_scores
|
910 |
if len(scores) == 1:
|
911 |
+
return copy.deepcopy(scores)
|
912 |
are_consistent = True
|
913 |
ticks = scores[0][0]
|
914 |
iscore = 1
|
|
|
929 |
|
930 |
|
931 |
###########################################################################
|
|
|
932 |
def _decode(trackdata=b'', exclude=None, include=None,
|
933 |
+
event_callback=None, exclusive_event_callback=None, no_eot_magic=False):
|
934 |
r'''Decodes MIDI track data into an opus-style list of events.
|
935 |
The options:
|
936 |
'exclude' is a list of event types which will be ignored SHOULD BE A SET
|
|
|
950 |
exclude = set(exclude)
|
951 |
|
952 |
# Pointer = 0; not used here; we eat through the bytearray instead.
|
953 |
+
event_code = -1; # used for running status
|
954 |
event_count = 0;
|
955 |
events = []
|
956 |
|
957 |
+
while (len(trackdata)):
|
958 |
# loop while there's anything to analyze ...
|
959 |
+
eot = False # When True, the event registrar aborts this loop
|
960 |
event_count += 1
|
961 |
|
962 |
E = []
|
963 |
# E for events - we'll feed it to the event registrar at the end.
|
964 |
|
965 |
# Slice off the delta time code, and analyze it
|
966 |
+
[time, trackdata] = _unshift_ber_int(trackdata)
|
967 |
|
968 |
# Now let's see what we can make of the command
|
969 |
+
first_byte = trackdata[0] & 0xFF
|
970 |
+
trackdata = trackdata[1:]
|
971 |
if (first_byte < 0xF0): # It's a MIDI event
|
972 |
if (first_byte & 0x80):
|
973 |
event_code = first_byte
|
|
|
981 |
command = event_code & 0xF0
|
982 |
channel = event_code & 0x0F
|
983 |
|
984 |
+
if (command == 0xF6): # 0-byte argument
|
985 |
pass
|
986 |
+
elif (command == 0xC0 or command == 0xD0): # 1-byte argument
|
987 |
+
parameter = trackdata[0] # could be B
|
988 |
+
trackdata = trackdata[1:]
|
989 |
+
else: # 2-byte argument could be BB or 14-bit
|
990 |
+
parameter = (trackdata[0], trackdata[1])
|
991 |
+
trackdata = trackdata[2:]
|
992 |
|
993 |
#################################################################
|
994 |
# MIDI events
|
995 |
|
996 |
+
if (command == 0x80):
|
997 |
if 'note_off' in exclude:
|
998 |
continue
|
999 |
E = ['note_off', time, channel, parameter[0], parameter[1]]
|
|
|
1004 |
elif (command == 0xA0):
|
1005 |
if 'key_after_touch' in exclude:
|
1006 |
continue
|
1007 |
+
E = ['key_after_touch', time, channel, parameter[0], parameter[1]]
|
1008 |
elif (command == 0xB0):
|
1009 |
if 'control_change' in exclude:
|
1010 |
continue
|
1011 |
+
E = ['control_change', time, channel, parameter[0], parameter[1]]
|
1012 |
elif (command == 0xC0):
|
1013 |
if 'patch_change' in exclude:
|
1014 |
continue
|
|
|
1021 |
if 'pitch_wheel_change' in exclude:
|
1022 |
continue
|
1023 |
E = ['pitch_wheel_change', time, channel,
|
1024 |
+
_read_14_bit(parameter) - 0x2000]
|
1025 |
else:
|
1026 |
+
_warn("Shouldn't get here; command=" + hex(command))
|
1027 |
|
1028 |
elif (first_byte == 0xFF): # It's a Meta-Event! ##################
|
1029 |
+
# [command, length, remainder] =
|
1030 |
# unpack("xCwa*", substr(trackdata, $Pointer, 6));
|
1031 |
+
# Pointer += 6 - len(remainder);
|
1032 |
# # Move past JUST the length-encoded.
|
1033 |
+
command = trackdata[0] & 0xFF
|
1034 |
+
trackdata = trackdata[1:]
|
1035 |
[length, trackdata] = _unshift_ber_int(trackdata)
|
1036 |
+
if (command == 0x00):
|
1037 |
+
if (length == 2):
|
1038 |
+
E = ['set_sequence_number', time, _twobytes2int(trackdata)]
|
1039 |
+
else:
|
1040 |
+
_warn('set_sequence_number: length must be 2, not ' + str(length))
|
1041 |
+
E = ['set_sequence_number', time, 0]
|
1042 |
+
|
1043 |
+
elif command >= 0x01 and command <= 0x0f: # Text events
|
1044 |
# 6.2 take it in bytes; let the user get the right encoding.
|
1045 |
# text_str = trackdata[0:length].decode('ascii','ignore')
|
1046 |
# text_str = trackdata[0:length].decode('ISO-8859-1')
|
1047 |
# 6.4 take it in bytes; let the user get the right encoding.
|
1048 |
+
text_data = bytes(trackdata[0:length]) # 6.4
|
1049 |
# Defined text events
|
1050 |
if (command == 0x01):
|
1051 |
+
E = ['text_event', time, text_data]
|
1052 |
elif (command == 0x02):
|
1053 |
+
E = ['copyright_text_event', time, text_data]
|
1054 |
elif (command == 0x03):
|
1055 |
+
E = ['track_name', time, text_data]
|
1056 |
elif (command == 0x04):
|
1057 |
+
E = ['instrument_name', time, text_data]
|
1058 |
elif (command == 0x05):
|
1059 |
+
E = ['lyric', time, text_data]
|
1060 |
elif (command == 0x06):
|
1061 |
+
E = ['marker', time, text_data]
|
1062 |
elif (command == 0x07):
|
1063 |
+
E = ['cue_point', time, text_data]
|
1064 |
# Reserved but apparently unassigned text events
|
1065 |
elif (command == 0x08):
|
1066 |
+
E = ['text_event_08', time, text_data]
|
1067 |
elif (command == 0x09):
|
1068 |
+
E = ['text_event_09', time, text_data]
|
1069 |
elif (command == 0x0a):
|
1070 |
+
E = ['text_event_0a', time, text_data]
|
1071 |
elif (command == 0x0b):
|
1072 |
+
E = ['text_event_0b', time, text_data]
|
1073 |
elif (command == 0x0c):
|
1074 |
+
E = ['text_event_0c', time, text_data]
|
1075 |
elif (command == 0x0d):
|
1076 |
+
E = ['text_event_0d', time, text_data]
|
1077 |
elif (command == 0x0e):
|
1078 |
+
E = ['text_event_0e', time, text_data]
|
1079 |
elif (command == 0x0f):
|
1080 |
+
E = ['text_event_0f', time, text_data]
|
1081 |
|
1082 |
# Now the sticky events -------------------------------------
|
1083 |
elif (command == 0x2F):
|
1084 |
+
E = ['end_track', time]
|
1085 |
+
# The code for handling this, oddly, comes LATER,
|
1086 |
+
# in the event registrar.
|
1087 |
+
elif (command == 0x51): # DTime, Microseconds/Crochet
|
1088 |
+
if length != 3:
|
1089 |
+
_warn('set_tempo event, but length=' + str(length))
|
1090 |
+
E = ['set_tempo', time,
|
1091 |
+
struct.unpack(">I", b'\x00' + trackdata[0:3])[0]]
|
1092 |
elif (command == 0x54):
|
1093 |
+
if length != 5: # DTime, HR, MN, SE, FR, FF
|
1094 |
+
_warn('smpte_offset event, but length=' + str(length))
|
1095 |
+
E = ['smpte_offset', time] + list(struct.unpack(">BBBBB", trackdata[0:5]))
|
1096 |
elif (command == 0x58):
|
1097 |
+
if length != 4: # DTime, NN, DD, CC, BB
|
1098 |
+
_warn('time_signature event, but length=' + str(length))
|
1099 |
+
E = ['time_signature', time] + list(trackdata[0:4])
|
1100 |
elif (command == 0x59):
|
1101 |
+
if length != 2: # DTime, SF(signed), MI
|
1102 |
+
_warn('key_signature event, but length=' + str(length))
|
1103 |
+
E = ['key_signature', time] + list(struct.unpack(">bB", trackdata[0:2]))
|
1104 |
+
elif (command == 0x7F): # 6.4
|
1105 |
+
E = ['sequencer_specific', time, bytes(trackdata[0:length])]
|
1106 |
else:
|
1107 |
+
E = ['raw_meta_event', time, command,
|
1108 |
+
bytes(trackdata[0:length])] # 6.0
|
1109 |
+
# "[uninterpretable meta-event command of length length]"
|
1110 |
+
# DTime, Command, Binary Data
|
1111 |
+
# It's uninterpretable; record it as raw_data.
|
1112 |
|
1113 |
# Pointer += length; # Now move Pointer
|
1114 |
trackdata = trackdata[length:]
|
|
|
1125 |
# is omitted if this is a non-final block in a multiblock sysex;
|
1126 |
# but the F7 (if there) is counted in the message's declared
|
1127 |
# length, so we don't have to think about it anyway.)
|
1128 |
+
# command = trackdata.pop(0)
|
1129 |
[length, trackdata] = _unshift_ber_int(trackdata)
|
1130 |
if first_byte == 0xF0:
|
1131 |
# 20091008 added ISO-8859-1 to get an 8-bit str
|
|
|
1149 |
# from the MIDI file spec. So, I'm going to assume that
|
1150 |
# they CAN, in practice, occur. I don't know whether it's
|
1151 |
# proper for you to actually emit these into a MIDI file.
|
1152 |
+
|
1153 |
+
elif (first_byte == 0xF2): # DTime, Beats
|
1154 |
# <song position msg> ::= F2 <data pair>
|
1155 |
E = ['song_position', time, _read_14_bit(trackdata[:2])]
|
1156 |
trackdata = trackdata[2:]
|
1157 |
|
1158 |
+
elif (first_byte == 0xF3): # <song select msg> ::= F3 <data singlet>
|
1159 |
# E = ['song_select', time, struct.unpack('>B',trackdata.pop(0))[0]]
|
1160 |
E = ['song_select', time, trackdata[0]]
|
1161 |
trackdata = trackdata[1:]
|
1162 |
# DTime, Thing (what?! song number? whatever ...)
|
1163 |
|
1164 |
+
elif (first_byte == 0xF6): # DTime
|
1165 |
E = ['tune_request', time]
|
1166 |
# What would a tune request be doing in a MIDI /file/?
|
1167 |
|
1168 |
+
#########################################################
|
1169 |
+
# ADD MORE META-EVENTS HERE. TODO:
|
1170 |
+
# f1 -- MTC Quarter Frame Message. One data byte follows
|
1171 |
+
# the Status; it's the time code value, from 0 to 127.
|
1172 |
+
# f8 -- MIDI clock. no data.
|
1173 |
+
# fa -- MIDI start. no data.
|
1174 |
+
# fb -- MIDI continue. no data.
|
1175 |
+
# fc -- MIDI stop. no data.
|
1176 |
+
# fe -- Active sense. no data.
|
1177 |
+
# f4 f5 f9 fd -- unallocated
|
1178 |
|
1179 |
r'''
|
1180 |
elif (first_byte > 0xF0) { # Some unknown kinda F-series event ####
|
|
|
1189 |
elif first_byte > 0xF0: # Some unknown F-series event
|
1190 |
# Here we only produce a one-byte piece of raw data.
|
1191 |
# E = ['raw_data', time, bytest(trackdata[0])] # 6.4
|
1192 |
+
E = ['raw_data', time, trackdata[0]] # 6.4 6.7
|
1193 |
trackdata = trackdata[1:]
|
1194 |
else: # Fallthru.
|
1195 |
+
_warn("Aborting track. Command-byte first_byte=" + hex(first_byte))
|
1196 |
break
|
1197 |
# End of the big if-group
|
1198 |
|
|
|
1199 |
######################################################################
|
1200 |
# THE EVENT REGISTRAR...
|
1201 |
+
if E and (E[0] == 'end_track'):
|
1202 |
# This is the code for exceptional handling of the EOT event.
|
1203 |
eot = True
|
1204 |
if not no_eot_magic:
|
1205 |
if E[1] > 0: # a null text-event to carry the delta-time
|
1206 |
E = ['text_event', E[1], '']
|
1207 |
else:
|
1208 |
+
E = [] # EOT with a delta-time of 0; ignore it.
|
1209 |
+
|
1210 |
if E and not (E[0] in exclude):
|
1211 |
+
# if ( $exclusive_event_callback ):
|
1212 |
# &{ $exclusive_event_callback }( @E );
|
1213 |
+
# else:
|
1214 |
# &{ $event_callback }( @E ) if $event_callback;
|
1215 |
+
events.append(E)
|
1216 |
if eot:
|
1217 |
break
|
1218 |
|
|
|
1832 |
plt.close()
|
1833 |
|
1834 |
if return_plt:
|
1835 |
+
plt.close(fig)
|
1836 |
return fig
|
1837 |
|
1838 |
plt.show()
|
|
|
4609 |
|
4610 |
###################################################################################
|
4611 |
|
4612 |
+
def check_and_fix_tones_chord(tones_chord, use_full_chords=True):
|
4613 |
|
4614 |
tones_chord_combs = [list(comb) for i in range(len(tones_chord), 0, -1) for comb in combinations(tones_chord, i)]
|
4615 |
|
4616 |
+
if use_full_chords:
|
4617 |
+
CHORDS = ALL_CHORDS_FULL
|
4618 |
+
|
4619 |
+
else:
|
4620 |
+
CHORDS = ALL_CHORDS_SORTED
|
4621 |
+
|
4622 |
for c in tones_chord_combs:
|
4623 |
+
if c in CHORDS:
|
4624 |
checked_tones_chord = c
|
4625 |
break
|
4626 |
|
|
|
4633 |
|
4634 |
###################################################################################
|
4635 |
|
4636 |
+
def advanced_check_and_fix_tones_chord(tones_chord, high_pitch=0, use_full_chords=True):
|
4637 |
|
4638 |
tones_chord_combs = [list(comb) for i in range(len(tones_chord), 0, -1) for comb in combinations(tones_chord, i)]
|
4639 |
|
4640 |
+
if use_full_chords:
|
4641 |
+
CHORDS = ALL_CHORDS_FULL
|
4642 |
+
|
4643 |
+
else:
|
4644 |
+
CHORDS = ALL_CHORDS_SORTED
|
4645 |
+
|
4646 |
for c in tones_chord_combs:
|
4647 |
+
if c in CHORDS:
|
4648 |
tchord = c
|
4649 |
|
4650 |
if 0 < high_pitch < 128 and len(tchord) == 1:
|
|
|
4690 |
pitch_shift=0,
|
4691 |
ceil_timings=False,
|
4692 |
round_timings=False,
|
4693 |
+
legacy_timings=True
|
4694 |
):
|
4695 |
|
4696 |
esn = copy.deepcopy(enhanced_score_notes)
|
|
|
7883 |
chord = []
|
7884 |
|
7885 |
for cc in c:
|
|
|
7886 |
|
7887 |
+
if cc[channels_index] != 9:
|
7888 |
+
if cc[pitches_index] not in seen:
|
7889 |
+
|
7890 |
cc[channels_index] = 0
|
7891 |
cc[patches_index] = 0
|
7892 |
+
|
7893 |
chord.append(cc)
|
7894 |
seen.append(cc[pitches_index])
|
7895 |
+
|
7896 |
+
else:
|
7897 |
+
if keep_drums:
|
7898 |
+
if cc[pitches_index]+128 not in seen:
|
7899 |
chord.append(cc)
|
7900 |
+
seen.append(cc[pitches_index]+128)
|
7901 |
|
7902 |
sp_escore_notes.append(chord)
|
7903 |
|
|
|
9073 |
else:
|
9074 |
return None
|
9075 |
|
9076 |
+
###################################################################################
|
9077 |
+
|
9078 |
+
def escore_notes_durations_counter(escore_notes,
|
9079 |
+
min_duration=0,
|
9080 |
+
durations_index=2,
|
9081 |
+
channels_index=3
|
9082 |
+
):
|
9083 |
+
|
9084 |
+
escore = [e for e in escore_notes if e[channels_index] != 9]
|
9085 |
+
durs = [e[durations_index] for e in escore if e[durations_index] >= min_duration]
|
9086 |
+
zero_durs = sum([1 for e in escore if e[durations_index] == 0])
|
9087 |
+
|
9088 |
+
return [len(durs), len(escore), zero_durs, Counter(durs).most_common()]
|
9089 |
+
|
9090 |
+
###################################################################################
|
9091 |
+
|
9092 |
+
def count_bad_chords_in_chordified_score(chordified_score,
|
9093 |
+
pitches_index=4,
|
9094 |
+
patches_index=6,
|
9095 |
+
max_patch=127,
|
9096 |
+
use_full_chords=False
|
9097 |
+
):
|
9098 |
+
|
9099 |
+
if use_full_chords:
|
9100 |
+
CHORDS = ALL_CHORDS_FULL
|
9101 |
+
|
9102 |
+
else:
|
9103 |
+
CHORDS = ALL_CHORDS_SORTED
|
9104 |
+
|
9105 |
+
bad_chords_count = 0
|
9106 |
+
|
9107 |
+
for c in chordified_score:
|
9108 |
+
|
9109 |
+
cpitches = [e[pitches_index] for e in c if e[patches_index] <= max_patch]
|
9110 |
+
tones_chord = sorted(set([p % 12 for p in cpitches]))
|
9111 |
+
|
9112 |
+
if tones_chord:
|
9113 |
+
if tones_chord not in CHORDS:
|
9114 |
+
bad_chords_count += 1
|
9115 |
+
|
9116 |
+
return [bad_chords_count, len(chordified_score)]
|
9117 |
+
|
9118 |
+
###################################################################################
|
9119 |
+
|
9120 |
+
def needleman_wunsch_aligner(seq1,
|
9121 |
+
seq2,
|
9122 |
+
align_idx,
|
9123 |
+
gap_penalty=-1,
|
9124 |
+
match_score=2,
|
9125 |
+
mismatch_penalty=-1
|
9126 |
+
):
|
9127 |
+
|
9128 |
+
n = len(seq1)
|
9129 |
+
m = len(seq2)
|
9130 |
+
|
9131 |
+
score_matrix = [[0] * (m + 1) for _ in range(n + 1)]
|
9132 |
+
|
9133 |
+
for i in range(1, n + 1):
|
9134 |
+
score_matrix[i][0] = gap_penalty * i
|
9135 |
+
for j in range(1, m + 1):
|
9136 |
+
score_matrix[0][j] = gap_penalty * j
|
9137 |
+
|
9138 |
+
for i in range(1, n + 1):
|
9139 |
+
for j in range(1, m + 1):
|
9140 |
+
match = score_matrix[i-1][j-1] + (match_score if seq1[i-1][align_idx] == seq2[j-1][align_idx] else mismatch_penalty)
|
9141 |
+
delete = score_matrix[i-1][j] + gap_penalty
|
9142 |
+
insert = score_matrix[i][j-1] + gap_penalty
|
9143 |
+
score_matrix[i][j] = max(match, delete, insert)
|
9144 |
+
|
9145 |
+
align1, align2 = [], []
|
9146 |
+
i, j = n, m
|
9147 |
+
|
9148 |
+
while i > 0 and j > 0:
|
9149 |
+
|
9150 |
+
score = score_matrix[i][j]
|
9151 |
+
score_diag = score_matrix[i-1][j-1]
|
9152 |
+
score_up = score_matrix[i-1][j]
|
9153 |
+
score_left = score_matrix[i][j-1]
|
9154 |
+
|
9155 |
+
if score == score_diag + (match_score if seq1[i-1][align_idx] == seq2[j-1][align_idx] else mismatch_penalty):
|
9156 |
+
align1.append(seq1[i-1])
|
9157 |
+
align2.append(seq2[j-1])
|
9158 |
+
i -= 1
|
9159 |
+
j -= 1
|
9160 |
+
elif score == score_up + gap_penalty:
|
9161 |
+
align1.append(seq1[i-1])
|
9162 |
+
align2.append([None] * 6)
|
9163 |
+
i -= 1
|
9164 |
+
elif score == score_left + gap_penalty:
|
9165 |
+
align1.append([None] * 6)
|
9166 |
+
align2.append(seq2[j-1])
|
9167 |
+
j -= 1
|
9168 |
+
|
9169 |
+
while i > 0:
|
9170 |
+
align1.append(seq1[i-1])
|
9171 |
+
align2.append([None] * 6)
|
9172 |
+
i -= 1
|
9173 |
+
while j > 0:
|
9174 |
+
align1.append([None] * 6)
|
9175 |
+
align2.append(seq2[j-1])
|
9176 |
+
j -= 1
|
9177 |
+
|
9178 |
+
align1.reverse()
|
9179 |
+
align2.reverse()
|
9180 |
+
|
9181 |
+
return align1, align2
|
9182 |
+
|
9183 |
+
###################################################################################
|
9184 |
+
|
9185 |
+
def align_escore_notes_to_escore_notes(src_escore_notes,
|
9186 |
+
trg_escore_notes,
|
9187 |
+
recalculate_scores_timings=True,
|
9188 |
+
pitches_idx=4
|
9189 |
+
):
|
9190 |
+
|
9191 |
+
if recalculate_scores_timings:
|
9192 |
+
src_escore_notes = recalculate_score_timings(src_escore_notes)
|
9193 |
+
trg_escore_notes = recalculate_score_timings(trg_escore_notes)
|
9194 |
+
|
9195 |
+
src_align1, trg_align2 = needleman_wunsch_aligner(src_escore_notes, trg_escore_notes, pitches_idx)
|
9196 |
+
|
9197 |
+
aligned_scores = [[al[0], al[1]] for al in zip(src_align1, trg_align2) if al[0][0] is not None and al[1][0] is not None]
|
9198 |
+
|
9199 |
+
return aligned_scores
|
9200 |
+
|
9201 |
+
###################################################################################
|
9202 |
+
|
9203 |
+
def t_to_n(arr, si, t):
|
9204 |
+
|
9205 |
+
ct = 0
|
9206 |
+
ci = si
|
9207 |
+
|
9208 |
+
while ct + arr[ci][1] < t and ci < len(arr)-1:
|
9209 |
+
ct += arr[ci][1]
|
9210 |
+
ci += 1
|
9211 |
+
|
9212 |
+
return ci+1
|
9213 |
+
|
9214 |
+
###################################################################################
|
9215 |
+
|
9216 |
+
def max_sum_chunk_idxs(arr, t=255):
|
9217 |
+
|
9218 |
+
n = t_to_n(arr, 0, t)
|
9219 |
+
|
9220 |
+
if n > len(arr):
|
9221 |
+
return [0, n]
|
9222 |
+
|
9223 |
+
max_sum = 0
|
9224 |
+
max_sum_start_index = 0
|
9225 |
+
|
9226 |
+
max_sum_start_idxs = [0, len(arr), sum([a[0] for a in arr])]
|
9227 |
+
|
9228 |
+
for i in range(len(arr)):
|
9229 |
+
|
9230 |
+
n = t_to_n(arr, i, t)
|
9231 |
+
|
9232 |
+
current_sum = sum([a[0] for a in arr[i:n]])
|
9233 |
+
current_time = sum([a[1] for a in arr[i:n]])
|
9234 |
+
|
9235 |
+
if current_sum > max_sum and current_time <= t:
|
9236 |
+
max_sum = current_sum
|
9237 |
+
max_sum_start_idxs = [i, n, max_sum]
|
9238 |
+
|
9239 |
+
return max_sum_start_idxs
|
9240 |
+
|
9241 |
+
###################################################################################
|
9242 |
+
|
9243 |
+
def find_highest_density_escore_notes_chunk(escore_notes, max_chunk_time=512):
|
9244 |
+
|
9245 |
+
dscore = delta_score_notes(escore_notes)
|
9246 |
+
|
9247 |
+
cscore = chordify_score([d[1:] for d in dscore])
|
9248 |
+
|
9249 |
+
notes_counts = [[len(c), c[0][0]] for c in cscore]
|
9250 |
+
|
9251 |
+
msc_idxs = max_sum_chunk_idxs(notes_counts, max_chunk_time)
|
9252 |
+
|
9253 |
+
chunk_dscore = [['note'] + c for c in flatten(cscore[msc_idxs[0]:msc_idxs[1]])]
|
9254 |
+
|
9255 |
+
chunk_escore = recalculate_score_timings(delta_score_to_abs_score(chunk_dscore))
|
9256 |
+
|
9257 |
+
return chunk_escore
|
9258 |
+
|
9259 |
+
###################################################################################
|
9260 |
#
|
9261 |
# This is the end of the TMIDI X Python module
|
9262 |
#
|