avans06/Audio-To-MIDI-And-Advanced-Renderer
37
1#! /usr/bin/python32 3import os4import re5import json6import math7import tqdm8import copy9import psutil10import shutil11import random12import hashlib13import secrets14import statistics15import multiprocessing16from src import MIDI17 18from array import array19from pathlib import Path20from fnmatch import fnmatch21from collections import Counter22from collections import defaultdict23from collections import OrderedDict24from difflib import SequenceMatcher as SM25from operator import itemgetter26from itertools import product, combinations, groupby27 28###################################################################################29 30# TMIDI X Code is below31 32###################################################################################33 34def Optimus_MIDI_TXT_Processor(MIDI_file, 35 line_by_line_output=True, 36 chordify_TXT=False,37 dataset_MIDI_events_time_denominator=1,38 output_velocity=True,39 output_MIDI_channels = False, 40 MIDI_channel=0, 41 MIDI_patch=[0, 1], 42 char_offset = 30000,43 transpose_by = 0,44 flip=False, 45 melody_conditioned_encoding=False,46 melody_pitch_baseline = 0,47 number_of_notes_to_sample = -1,48 sampling_offset_from_start = 0,49 karaoke=False,50 karaoke_language_encoding='utf-8',51 song_name='Song',52 perfect_timings=False,53 musenet_encoding=False,54 transform=0,55 zero_token=False,56 reset_timings=False):57 58 '''Project Los Angeles59 Tegridy Code 2021'''60 61###########62 63 debug = False64 65 ev = 066 67 chords_list_final = []68 chords_list = []69 events_matrix = []70 melody = []71 melody1 = []72 73 itrack = 174 75 min_note = 076 max_note = 077 ev = 078 patch = 079 80 score = []81 rec_event = []82 83 txt = ''84 txtc = ''85 chords = []86 melody_chords = []87 88 karaoke_events_matrix = []89 karaokez = []90 91 sample = 092 start_sample = 093 94 bass_melody = []95 96 INTS = []97 bints = 098 99########### 100 101 def list_average(num):102 sum_num = 0103 for t in num:104 sum_num = sum_num + t 105 106 avg = sum_num / len(num)107 return avg108 109###########110 111 #print('Loading MIDI file...')112 midi_file = open(MIDI_file, 'rb')113 if debug: print('Processing File:', MIDI_file)114 115 try:116 opus = MIDI.midi2opus(midi_file.read())117 118 except:119 print('Problematic MIDI. Skipping...')120 print('File name:', MIDI_file)121 midi_file.close()122 return txt, melody, chords123 124 midi_file.close()125 126 score1 = MIDI.to_millisecs(opus)127 score2 = MIDI.opus2score(score1)128 129 # score2 = MIDI.opus2score(opus) # TODO Improve score timings when it will be possible.130 131 if MIDI_channel == 16: # Process all MIDI channels132 score = score2133 134 if MIDI_channel >= 0 and MIDI_channel <= 15: # Process only a selected single MIDI channel135 score = MIDI.grep(score2, [MIDI_channel])136 137 if MIDI_channel == -1: # Process all channels except drums (except channel 9)138 score = MIDI.grep(score2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]) 139 140 #print('Reading all MIDI events from the MIDI file...')141 while itrack < len(score):142 for event in score[itrack]:143 144 if perfect_timings:145 if event[0] == 'note':146 event[1] = round(event[1], -1)147 event[2] = round(event[2], -1)148 149 if event[0] == 'text_event' or event[0] == 'lyric' or event[0] == 'note':150 if perfect_timings:151 event[1] = round(event[1], -1)152 karaokez.append(event)153 154 if event[0] == 'text_event' or event[0] == 'lyric':155 if perfect_timings:156 event[1] = round(event[1], -1)157 try:158 event[2] = str(event[2].decode(karaoke_language_encoding, 'replace')).replace('/', '').replace(' ', '').replace('\\', '')159 except:160 event[2] = str(event[2]).replace('/', '').replace(' ', '').replace('\\', '')161 continue162 karaoke_events_matrix.append(event)163 164 if event[0] == 'patch_change':165 patch = event[3]166 167 if event[0] == 'note' and patch in MIDI_patch:168 if len(event) == 6: # Checking for bad notes...169 eve = copy.deepcopy(event)170 171 eve[1] = int(event[1] / dataset_MIDI_events_time_denominator)172 eve[2] = int(event[2] / dataset_MIDI_events_time_denominator)173 174 eve[4] = int(event[4] + transpose_by)175 176 if flip == True:177 eve[4] = int(127 - (event[4] + transpose_by)) 178 179 if number_of_notes_to_sample > -1:180 if sample <= number_of_notes_to_sample:181 if start_sample >= sampling_offset_from_start:182 events_matrix.append(eve)183 sample += 1184 ev += 1185 else:186 start_sample += 1187 188 else:189 events_matrix.append(eve)190 ev += 1191 start_sample += 1192 193 itrack +=1 # Going to next track...194 195 #print('Doing some heavy pythonic sorting...Please stand by...')196 197 fn = os.path.basename(MIDI_file)198 song_name = song_name.replace(' ', '_').replace('=', '_').replace('\'', '-')199 if song_name == 'Song':200 sng_name = fn.split('.')[0].replace(' ', '_').replace('=', '_').replace('\'', '-')201 song_name = sng_name202 203 # Zero token204 if zero_token:205 txt += chr(char_offset) + chr(char_offset)206 if output_MIDI_channels:207 txt += chr(char_offset)208 if output_velocity:209 txt += chr(char_offset) + chr(char_offset) 210 else:211 txt += chr(char_offset)212 213 txtc += chr(char_offset) + chr(char_offset)214 if output_MIDI_channels:215 txtc += chr(char_offset)216 if output_velocity:217 txtc += chr(char_offset) + chr(char_offset) 218 else:219 txtc += chr(char_offset)220 221 txt += '=' + song_name + '_with_' + str(len(events_matrix)-1) + '_notes'222 txtc += '=' + song_name + '_with_' + str(len(events_matrix)-1) + '_notes'223 224 else:225 # Song stamp226 txt += 'SONG=' + song_name + '_with_' + str(len(events_matrix)-1) + '_notes'227 txtc += 'SONG=' + song_name + '_with_' + str(len(events_matrix)-1) + '_notes'228 229 if line_by_line_output:230 txt += chr(10)231 txtc += chr(10)232 else:233 txt += chr(32)234 txtc += chr(32)235 236 #print('Sorting input by start time...')237 events_matrix.sort(key=lambda x: x[1]) # Sorting input by start time 238 239 #print('Timings converter')240 if reset_timings:241 ev_matrix = Tegridy_Timings_Converter(events_matrix)[0]242 else:243 ev_matrix = events_matrix244 245 chords.extend(ev_matrix)246 #print(chords)247 248 #print('Extracting melody...')249 melody_list = []250 251 #print('Grouping by start time. This will take a while...')252 values = set(map(lambda x:x[1], ev_matrix)) # Non-multithreaded function version just in case253 254 groups = [[y for y in ev_matrix if y[1]==x and len(y) == 6] for x in values] # Grouping notes into chords while discarting bad notes...255 256 #print('Sorting events...')257 for items in groups:258 259 items.sort(reverse=True, key=lambda x: x[4]) # Sorting events by pitch260 261 if melody_conditioned_encoding: items[0][3] = 0 # Melody should always bear MIDI Channel 0 for code to work262 263 melody_list.append(items[0]) # Creating final melody list264 melody_chords.append(items) # Creating final chords list265 bass_melody.append(items[-1]) # Creating final bass melody list266 267 # [WIP] Melody-conditioned chords list268 if melody_conditioned_encoding == True:269 if not karaoke:270 271 previous_event = copy.deepcopy(melody_chords[0][0])272 273 for ev in melody_chords:274 hp = True275 ev.sort(reverse=False, key=lambda x: x[4]) # Sorting chord events by pitch276 for event in ev:277 278 # Computing events details279 start_time = int(abs(event[1] - previous_event[1]))280 281 duration = int(previous_event[2])282 283 if hp == True:284 if int(previous_event[4]) >= melody_pitch_baseline:285 channel = int(0)286 hp = False287 else:288 channel = int(previous_event[3]+1)289 hp = False 290 else:291 channel = int(previous_event[3]+1)292 hp = False293 294 pitch = int(previous_event[4])295 296 velocity = int(previous_event[5])297 298 # Writing INTergerS...299 try:300 INTS.append([(start_time)+char_offset, (duration)+char_offset, channel+char_offset, pitch+char_offset, velocity+char_offset])301 except:302 bints += 1303 304 # Converting to TXT if possible...305 try:306 txtc += str(chr(start_time + char_offset))307 txtc += str(chr(duration + char_offset))308 txtc += str(chr(pitch + char_offset))309 if output_velocity:310 txtc += str(chr(velocity + char_offset))311 if output_MIDI_channels:312 txtc += str(chr(channel + char_offset))313 314 if line_by_line_output:315 316 317 txtc += chr(10)318 else:319 320 txtc += chr(32)321 322 previous_event = copy.deepcopy(event)323 324 except:325 # print('Problematic MIDI event! Skipping...')326 continue327 328 if not line_by_line_output:329 txtc += chr(10)330 331 txt = txtc332 chords = melody_chords333 334 # Default stuff (not melody-conditioned/not-karaoke)335 else: 336 if not karaoke:337 melody_chords.sort(reverse=False, key=lambda x: x[0][1])338 mel_chords = []339 for mc in melody_chords:340 mel_chords.extend(mc)341 342 if transform != 0: 343 chords = Tegridy_Transform(mel_chords, transform)344 else:345 chords = mel_chords346 347 # TXT Stuff348 previous_event = copy.deepcopy(chords[0])349 for event in chords:350 351 # Computing events details352 start_time = int(abs(event[1] - previous_event[1]))353 354 duration = int(previous_event[2])355 356 channel = int(previous_event[3])357 358 pitch = int(previous_event[4] + transpose_by)359 if flip == True:360 pitch = 127 - int(previous_event[4] + transpose_by)361 362 velocity = int(previous_event[5])363 364 # Writing INTergerS...365 try:366 INTS.append([(start_time)+char_offset, (duration)+char_offset, channel+char_offset, pitch+char_offset, velocity+char_offset])367 except:368 bints += 1369 370 # Converting to TXT if possible...371 try:372 txt += str(chr(start_time + char_offset))373 txt += str(chr(duration + char_offset))374 txt += str(chr(pitch + char_offset))375 if output_velocity:376 txt += str(chr(velocity + char_offset))377 if output_MIDI_channels:378 txt += str(chr(channel + char_offset))379 380 381 if chordify_TXT == True and int(event[1] - previous_event[1]) == 0:382 txt += '' 383 else: 384 if line_by_line_output:385 txt += chr(10)386 else:387 txt += chr(32) 388 389 previous_event = copy.deepcopy(event)390 391 except:392 # print('Problematic MIDI event. Skipping...')393 continue394 395 if not line_by_line_output:396 txt += chr(10) 397 398 # Karaoke stuff399 if karaoke:400 401 melody_chords.sort(reverse=False, key=lambda x: x[0][1])402 mel_chords = []403 for mc in melody_chords:404 mel_chords.extend(mc)405 406 if transform != 0: 407 chords = Tegridy_Transform(mel_chords, transform)408 else:409 chords = mel_chords410 411 previous_event = copy.deepcopy(chords[0])412 for event in chords:413 414 # Computing events details415 start_time = int(abs(event[1] - previous_event[1]))416 417 duration = int(previous_event[2])418 419 channel = int(previous_event[3])420 421 pitch = int(previous_event[4] + transpose_by)422 423 velocity = int(previous_event[5])424 425 # Converting to TXT426 txt += str(chr(start_time + char_offset))427 txt += str(chr(duration + char_offset))428 txt += str(chr(pitch + char_offset))429 430 txt += str(chr(velocity + char_offset))431 txt += str(chr(channel + char_offset)) 432 433 if start_time > 0:434 for k in karaoke_events_matrix:435 if event[1] == k[1]:436 txt += str('=')437 txt += str(k[2]) 438 break439 440 if line_by_line_output:441 txt += chr(10)442 else:443 txt += chr(32) 444 445 previous_event = copy.deepcopy(event)446 447 if not line_by_line_output:448 txt += chr(10)449 450 # Final processing code...451 # =======================================================================452 453 # Helper aux/backup function for Karaoke454 karaokez.sort(reverse=False, key=lambda x: x[1]) 455 456 # MuseNet sorting457 if musenet_encoding and not melody_conditioned_encoding and not karaoke:458 chords.sort(key=lambda x: (x[1], x[3]))459 460 # Final melody sort461 melody_list.sort()462 463 # auxs for future use464 aux1 = [None]465 aux2 = [None]466 467 return txt, melody_list, chords, bass_melody, karaokez, INTS, aux1, aux2 # aux1 and aux2 are not used atm468 469###################################################################################470 471def Optimus_TXT_to_Notes_Converter(Optimus_TXT_String,472 line_by_line_dataset = True,473 has_velocities = True,474 has_MIDI_channels = True,475 dataset_MIDI_events_time_denominator = 1,476 char_encoding_offset = 30000,477 save_only_first_composition = True,478 simulate_velocity=True,479 karaoke=False,480 zero_token=False):481 482 '''Project Los Angeles483 Tegridy Code 2020'''484 485 print('Tegridy Optimus TXT to Notes Converter')486 print('Converting TXT to Notes list...Please wait...')487 488 song_name = ''489 490 if line_by_line_dataset:491 input_string = Optimus_TXT_String.split('\n')492 else:493 input_string = Optimus_TXT_String.split(' ')494 495 if line_by_line_dataset:496 name_string = Optimus_TXT_String.split('\n')[0].split('=')497 else:498 name_string = Optimus_TXT_String.split(' ')[0].split('=')499 500 # Zero token501 zt = ''502 503 zt += chr(char_encoding_offset) + chr(char_encoding_offset)504 505 if has_MIDI_channels:506 zt += chr(char_encoding_offset)507 508 if has_velocities:509 zt += chr(char_encoding_offset) + chr(char_encoding_offset) 510 511 else:512 zt += chr(char_encoding_offset)513 514 if zero_token:515 if name_string[0] == zt:516 song_name = name_string[1]517 518 else:519 if name_string[0] == 'SONG':520 song_name = name_string[1]521 522 output_list = []523 st = 0524 525 for i in range(2, len(input_string)-1):526 527 if save_only_first_composition:528 if zero_token:529 if input_string[i].split('=')[0] == zt:530 531 song_name = name_string[1]532 break533 534 else:535 if input_string[i].split('=')[0] == 'SONG':536 537 song_name = name_string[1]538 break539 try:540 istring = input_string[i]541 542 if has_MIDI_channels == False:543 step = 4 544 545 if has_MIDI_channels == True:546 step = 5547 548 if has_velocities == False:549 step -= 1550 551 st += int(ord(istring[0]) - char_encoding_offset) * dataset_MIDI_events_time_denominator552 553 if not karaoke:554 for s in range(0, len(istring), step):555 if has_MIDI_channels==True:556 if step > 3 and len(istring) > 2:557 out = [] 558 out.append('note')559 560 out.append(st) # Start time561 562 out.append(int(ord(istring[s+1]) - char_encoding_offset) * dataset_MIDI_events_time_denominator) # Duration563 564 if has_velocities:565 out.append(int(ord(istring[s+4]) - char_encoding_offset)) # Channel566 else:567 out.append(int(ord(istring[s+3]) - char_encoding_offset)) # Channel 568 569 out.append(int(ord(istring[s+2]) - char_encoding_offset)) # Pitch570 571 if simulate_velocity:572 if s == 0:573 sim_vel = int(ord(istring[s+2]) - char_encoding_offset)574 out.append(sim_vel) # Simulated Velocity (= highest note's pitch)575 else: 576 out.append(int(ord(istring[s+3]) - char_encoding_offset)) # Velocity577 578 if has_MIDI_channels==False:579 if step > 3 and len(istring) > 2:580 out = [] 581 out.append('note')582 583 out.append(st) # Start time584 out.append(int(ord(istring[s+1]) - char_encoding_offset) * dataset_MIDI_events_time_denominator) # Duration585 out.append(0) # Channel586 out.append(int(ord(istring[s+2]) - char_encoding_offset)) # Pitch587 588 if simulate_velocity:589 if s == 0:590 sim_vel = int(ord(istring[s+2]) - char_encoding_offset)591 out.append(sim_vel) # Simulated Velocity (= highest note's pitch)592 else: 593 out.append(int(ord(istring[s+3]) - char_encoding_offset)) # Velocity594 595 if step == 3 and len(istring) > 2:596 out = [] 597 out.append('note')598 599 out.append(st) # Start time600 out.append(int(ord(istring[s+1]) - char_encoding_offset) * dataset_MIDI_events_time_denominator) # Duration601 out.append(0) # Channel602 out.append(int(ord(istring[s+2]) - char_encoding_offset)) # Pitch603 604 out.append(int(ord(istring[s+2]) - char_encoding_offset)) # Velocity = Pitch605 606 output_list.append(out)607 608 if karaoke:609 try:610 out = [] 611 out.append('note')612 613 out.append(st) # Start time614 out.append(int(ord(istring[1]) - char_encoding_offset) * dataset_MIDI_events_time_denominator) # Duration615 out.append(int(ord(istring[4]) - char_encoding_offset)) # Channel616 out.append(int(ord(istring[2]) - char_encoding_offset)) # Pitch617 618 if simulate_velocity:619 if s == 0:620 sim_vel = int(ord(istring[2]) - char_encoding_offset)621 out.append(sim_vel) # Simulated Velocity (= highest note's pitch)622 else: 623 out.append(int(ord(istring[3]) - char_encoding_offset)) # Velocity624 output_list.append(out)625 out = []626 if istring.split('=')[1] != '':627 out.append('lyric')628 out.append(st)629 out.append(istring.split('=')[1])630 output_list.append(out)631 except:632 continue633 634 635 except:636 print('Bad note string:', istring)637 continue638 639 # Simple error control just in case640 S = []641 for x in output_list:642 if len(x) == 6 or len(x) == 3:643 S.append(x)644 645 output_list.clear() 646 output_list = copy.deepcopy(S)647 648 649 print('Task complete! Enjoy! :)')650 651 return output_list, song_name652 653###################################################################################654 655def Optimus_Data2TXT_Converter(data,656 dataset_time_denominator=1,657 transpose_by = 0,658 char_offset = 33,659 line_by_line_output = True,660 output_velocity = False,661 output_MIDI_channels = False):662 663 664 '''Input: data as a flat chords list of flat chords lists665 666 Output: TXT string667 INTs668 669 Project Los Angeles670 Tegridy Code 2021'''671 672 txt = ''673 TXT = ''674 675 quit = False676 counter = 0677 678 INTs = []679 INTs_f = []680 681 for d in tqdm.tqdm(sorted(data)):682 683 if quit == True:684 break685 686 txt = 'SONG=' + str(counter)687 counter += 1688 689 if line_by_line_output:690 txt += chr(10)691 else:692 txt += chr(32)693 694 INTs = []695 696 # TXT Stuff697 previous_event = copy.deepcopy(d[0])698 for event in sorted(d):699 700 # Computing events details701 start_time = int(abs(event[1] - previous_event[1]) / dataset_time_denominator)702 703 duration = int(previous_event[2] / dataset_time_denominator)704 705 channel = int(previous_event[3])706 707 pitch = int(previous_event[4] + transpose_by)708 709 velocity = int(previous_event[5])710 711 INTs.append([start_time, duration, pitch])712 713 # Converting to TXT if possible...714 try:715 txt += str(chr(start_time + char_offset))716 txt += str(chr(duration + char_offset))717 txt += str(chr(pitch + char_offset))718 if output_velocity:719 txt += str(chr(velocity + char_offset))720 if output_MIDI_channels:721 txt += str(chr(channel + char_offset))722 723 if line_by_line_output:724 txt += chr(10)725 else:726 txt += chr(32) 727 728 previous_event = copy.deepcopy(event)729 except KeyboardInterrupt:730 quit = True731 break732 except:733 print('Problematic MIDI data. Skipping...')734 continue735 736 if not line_by_line_output:737 txt += chr(10)738 739 TXT += txt740 INTs_f.extend(INTs)741 742 return TXT, INTs_f743 744###################################################################################745 746def Optimus_Squash(chords_list, simulate_velocity=True, mono_compression=False):747 748 '''Input: Flat chords list749 Simulate velocity or not750 Mono-compression enabled or disabled751 752 Default is almost lossless 25% compression, otherwise, lossy 50% compression (mono-compression)753 754 Output: Squashed chords list755 Resulting compression level756 757 Please note that if drums are passed through as is758 759 Project Los Angeles760 Tegridy Code 2021'''761 762 output = []763 ptime = 0764 vel = 0765 boost = 15766 stptc = []767 ocount = 0768 rcount = 0769 770 for c in chords_list:771 772 cc = copy.deepcopy(c)773 ocount += 1774 775 if [cc[1], cc[3], (cc[4] % 12) + 60] not in stptc:776 stptc.append([cc[1], cc[3], (cc[4] % 12) + 60])777 778 if cc[3] != 9:779 cc[4] = (c[4] % 12) + 60780 781 if simulate_velocity and c[1] != ptime:782 vel = c[4] + boost783 784 if cc[3] != 9:785 cc[5] = vel786 787 if mono_compression:788 if c[1] != ptime:789 output.append(cc)790 rcount += 1 791 else:792 output.append(cc)793 rcount += 1794 795 ptime = c[1]796 797 output.sort(key=lambda x: (x[1], x[4]))798 799 comp_level = 100 - int((rcount * 100) / ocount)800 801 return output, comp_level802 803###################################################################################804 805def Optimus_Signature(chords_list, calculate_full_signature=False):806 807 '''Optimus Signature808 809 ---In the name of the search for a perfect score slice signature---810 811 Input: Flat chords list to evaluate812 813 Output: Full Optimus Signature as a list814 Best/recommended Optimus Signature as a list815 816 Project Los Angeles817 Tegridy Code 2021'''818 819 # Pitches820 821 ## StDev822 if calculate_full_signature:823 psd = statistics.stdev([int(y[4]) for y in chords_list])824 else:825 psd = 0826 827 ## Median828 pmh = statistics.median_high([int(y[4]) for y in chords_list])829 pm = statistics.median([int(y[4]) for y in chords_list])830 pml = statistics.median_low([int(y[4]) for y in chords_list])831 832 ## Mean833 if calculate_full_signature:834 phm = statistics.harmonic_mean([int(y[4]) for y in chords_list])835 else:836 phm = 0837 838 # Durations839 dur = statistics.median([int(y[2]) for y in chords_list])840 841 # Velocities842 843 vel = statistics.median([int(y[5]) for y in chords_list])844 845 # Beats846 mtds = statistics.median([int(abs(chords_list[i-1][1]-chords_list[i][1])) for i in range(1, len(chords_list))])847 if calculate_full_signature:848 hmtds = statistics.harmonic_mean([int(abs(chords_list[i-1][1]-chords_list[i][1])) for i in range(1, len(chords_list))])849 else:850 hmtds = 0851 852 # Final Optimus signatures853 full_Optimus_signature = [round(psd), round(pmh), round(pm), round(pml), round(phm), round(dur), round(vel), round(mtds), round(hmtds)]854 ######################## PStDev PMedianH PMedian PMedianL PHarmoMe Duration Velocity Beat HarmoBeat855 856 best_Optimus_signature = [round(pmh), round(pm), round(pml), round(dur, -1), round(vel, -1), round(mtds, -1)]857 ######################## PMedianH PMedian PMedianL Duration Velocity Beat858 859 # Return...860 return full_Optimus_signature, best_Optimus_signature861 862 863###################################################################################864#865# TMIDI 2.0 Helper functions866#867###################################################################################868 869def Tegridy_FastSearch(needle, haystack, randomize = False):870 871 '''872 873 Input: Needle iterable874 Haystack iterable875 Randomize search range (this prevents determinism)876 877 Output: Start index of the needle iterable in a haystack iterable878 If nothing found, -1 is returned879 880 Project Los Angeles881 Tegridy Code 2021'''882 883 need = copy.deepcopy(needle)884 885 try:886 if randomize:887 idx = haystack.index(need, secrets.randbelow(len(haystack)-len(need)))888 else:889 idx = haystack.index(need)890 891 except KeyboardInterrupt:892 return -1893 894 except:895 return -1896 897 return idx898 899###################################################################################900 901def Tegridy_Chord_Match(chord1, chord2, match_type=2):902 903 '''Tegridy Chord Match904 905 Input: Two chords to evaluate906 Match type: 2 = duration, channel, pitch, velocity907 3 = channel, pitch, velocity908 4 = pitch, velocity909 5 = velocity910 911 Output: Match rating (0-100)912 NOTE: Match rating == -1 means identical source chords913 NOTE: Match rating == 100 means mutual shortest chord914 915 Project Los Angeles916 Tegridy Code 2021'''917 918 match_rating = 0919 920 if chord1 == []:921 return 0922 if chord2 == []:923 return 0924 925 if chord1 == chord2:926 return -1927 928 else:929 zipped_pairs = list(zip(chord1, chord2))930 zipped_diff = abs(len(chord1) - len(chord2))931 932 short_match = [False]933 for pair in zipped_pairs:934 cho1 = ' '.join([str(y) for y in pair[0][match_type:]])935 cho2 = ' '.join([str(y) for y in pair[1][match_type:]])936 if cho1 == cho2:937 short_match.append(True)938 else:939 short_match.append(False)940 941 if True in short_match:942 return 100943 944 pairs_ratings = []945 946 for pair in zipped_pairs:947 cho1 = ' '.join([str(y) for y in pair[0][match_type:]])948 cho2 = ' '.join([str(y) for y in pair[1][match_type:]])949 pairs_ratings.append(SM(None, cho1, cho2).ratio())950 951 match_rating = sum(pairs_ratings) / len(pairs_ratings) * 100952 953 return match_rating954 955###################################################################################956 957def Tegridy_Last_Chord_Finder(chords_list):958 959 '''Tegridy Last Chord Finder960 961 Input: Flat chords list962 963 Output: Last detected chord of the chords list964 Last chord start index in the original chords list965 First chord end index in the original chords list966 967 Project Los Angeles968 Tegridy Code 2021'''969 970 chords = []971 cho = []972 973 ptime = 0974 975 i = 0976 977 pc_idx = 0978 fc_idx = 0979 980 chords_list.sort(reverse=False, key=lambda x: x[1])981 982 for cc in chords_list:983 984 if cc[1] == ptime:985 986 cho.append(cc)987 988 ptime = cc[1]989 990 else:991 if pc_idx == 0: 992 fc_idx = chords_list.index(cc)993 pc_idx = chords_list.index(cc)994 995 chords.append(cho)996 997 cho = []998 999 cho.append(cc)1000 1001 ptime = cc[1]1002 1003 i += 11004 1005 if cho != []: 1006 chords.append(cho)1007 i += 11008 1009 return chords_list[pc_idx:], pc_idx, fc_idx1010 1011###################################################################################1012 1013def Tegridy_Chords_Generator(chords_list, shuffle_pairs = True, remove_single_notes=False):1014 1015 '''Tegridy Score Chords Pairs Generator1016 1017 Input: Flat chords list1018 Shuffle pairs (recommended)1019 1020 Output: List of chords1021 1022 Average time(ms) per chord1023 Average time(ms) per pitch1024 Average chords delta time1025 1026 Average duration1027 Average channel1028 Average pitch1029 Average velocity1030 1031 Project Los Angeles1032 Tegridy Code 2021'''1033 1034 chords = []1035 cho = []1036 1037 i = 01038 1039 # Sort by start time1040 chords_list.sort(reverse=False, key=lambda x: x[1])1041 1042 # Main loop1043 pcho = chords_list[0]1044 for cc in chords_list:1045 if cc[1] == pcho[1]:1046 1047 cho.append(cc)1048 pcho = copy.deepcopy(cc)1049 1050 else:1051 if not remove_single_notes:1052 chords.append(cho)1053 cho = []1054 cho.append(cc)1055 pcho = copy.deepcopy(cc)1056 1057 i += 11058 else:1059 if len(cho) > 1:1060 chords.append(cho)1061 cho = []1062 cho.append(cc)1063 pcho = copy.deepcopy(cc)1064 1065 i += 1 1066 1067 # Averages1068 t0 = chords[0][0][1]1069 t1 = chords[-1][-1][1]1070 tdel = abs(t1 - t0)1071 avg_ms_per_chord = int(tdel / i)1072 avg_ms_per_pitch = int(tdel / len(chords_list))1073 1074 # Delta time1075 tds = [int(abs(chords_list[i-1][1]-chords_list[i][1]) / 1) for i in range(1, len(chords_list))]1076 if len(tds) != 0: avg_delta_time = int(sum(tds) / len(tds))1077 1078 # Chords list attributes1079 p = int(sum([int(y[4]) for y in chords_list]) / len(chords_list))1080 d = int(sum([int(y[2]) for y in chords_list]) / len(chords_list))1081 c = int(sum([int(y[3]) for y in chords_list]) / len(chords_list))1082 v = int(sum([int(y[5]) for y in chords_list]) / len(chords_list))1083 1084 # Final shuffle1085 if shuffle_pairs:1086 random.shuffle(chords)1087 1088 return chords, [avg_ms_per_chord, avg_ms_per_pitch, avg_delta_time], [d, c, p, v]1089 1090###################################################################################1091 1092def Tegridy_Chords_List_Music_Features(chords_list, st_dur_div = 1, pitch_div = 1, vel_div = 1):1093 1094 '''Tegridy Chords List Music Features1095 1096 Input: Flat chords list1097 1098 Output: A list of the extracted chords list's music features1099 1100 Project Los Angeles1101 Tegridy Code 2021'''1102 1103 chords_list1 = [x for x in chords_list if x]1104 chords_list1.sort(reverse=False, key=lambda x: x[1])1105 1106 # Features extraction code1107 1108 melody_list = []1109 bass_melody = []1110 melody_chords = []1111 mel_avg_tds = []1112 mel_chrd_avg_tds = []1113 bass_melody_avg_tds = []1114 1115 #print('Grouping by start time. This will take a while...')1116 values = set(map(lambda x:x[1], chords_list1)) # Non-multithreaded function version just in case1117 1118 groups = [[y for y in chords_list1 if y[1]==x and len(y) == 6] for x in values] # Grouping notes into chords while discarting bad notes...1119 1120 #print('Sorting events...')1121 for items in groups:1122 items.sort(reverse=True, key=lambda x: x[4]) # Sorting events by pitch1123 melody_list.append(items[0]) # Creating final melody list1124 melody_chords.append(items) # Creating final chords list1125 bass_melody.append(items[-1]) # Creating final bass melody list1126 1127 #print('Final sorting by start time...') 1128 melody_list.sort(reverse=False, key=lambda x: x[1]) # Sorting events by start time1129 melody_chords.sort(reverse=False, key=lambda x: x[0][1]) # Sorting events by start time1130 bass_melody.sort(reverse=False, key=lambda x: x[1]) # Sorting events by start time1131 1132 # Extracting music features from the chords list1133 1134 # Melody features1135 mel_avg_pitch = int(sum([y[4] for y in melody_list]) / len(melody_list) / pitch_div)1136 mel_avg_dur = int(sum([int(y[2] / st_dur_div) for y in melody_list]) / len(melody_list))1137 mel_avg_vel = int(sum([int(y[5] / vel_div) for y in melody_list]) / len(melody_list))1138 mel_avg_chan = int(sum([int(y[3]) for y in melody_list]) / len(melody_list))1139 1140 mel_tds = [int(abs(melody_list[i-1][1]-melody_list[i][1])) for i in range(1, len(melody_list))]1141 if len(mel_tds) != 0: mel_avg_tds = int(sum(mel_tds) / len(mel_tds) / st_dur_div)1142 1143 melody_features = [mel_avg_tds, mel_avg_dur, mel_avg_chan, mel_avg_pitch, mel_avg_vel]1144 1145 # Chords list features1146 mel_chrd_avg_pitch = int(sum([y[4] for y in chords_list1]) / len(chords_list1) / pitch_div)1147 mel_chrd_avg_dur = int(sum([int(y[2] / st_dur_div) for y in chords_list1]) / len(chords_list1))1148 mel_chrd_avg_vel = int(sum([int(y[5] / vel_div) for y in chords_list1]) / len(chords_list1))1149 mel_chrd_avg_chan = int(sum([int(y[3]) for y in chords_list1]) / len(chords_list1))1150 1151 mel_chrd_tds = [int(abs(chords_list1[i-1][1]-chords_list1[i][1])) for i in range(1, len(chords_list1))]1152 if len(mel_tds) != 0: mel_chrd_avg_tds = int(sum(mel_chrd_tds) / len(mel_chrd_tds) / st_dur_div)1153 1154 chords_list_features = [mel_chrd_avg_tds, mel_chrd_avg_dur, mel_chrd_avg_chan, mel_chrd_avg_pitch, mel_chrd_avg_vel]1155 1156 # Bass melody features1157 bass_melody_avg_pitch = int(sum([y[4] for y in bass_melody]) / len(bass_melody) / pitch_div)1158 bass_melody_avg_dur = int(sum([int(y[2] / st_dur_div) for y in bass_melody]) / len(bass_melody))1159 bass_melody_avg_vel = int(sum([int(y[5] / vel_div) for y in bass_melody]) / len(bass_melody))1160 bass_melody_avg_chan = int(sum([int(y[3]) for y in bass_melody]) / len(bass_melody))1161 1162 bass_melody_tds = [int(abs(bass_melody[i-1][1]-bass_melody[i][1])) for i in range(1, len(bass_melody))]1163 if len(bass_melody_tds) != 0: bass_melody_avg_tds = int(sum(bass_melody_tds) / len(bass_melody_tds) / st_dur_div)1164 1165 bass_melody_features = [bass_melody_avg_tds, bass_melody_avg_dur, bass_melody_avg_chan, bass_melody_avg_pitch, bass_melody_avg_vel]1166 1167 # A list to return all features1168 music_features = []1169 1170 music_features.extend([len(chords_list1)]) # Count of the original chords list notes1171 1172 music_features.extend(melody_features) # Extracted melody features1173 music_features.extend(chords_list_features) # Extracted chords list features1174 music_features.extend(bass_melody_features) # Extracted bass melody features1175 music_features.extend([sum([y[4] for y in chords_list1])]) # Sum of all pitches in the original chords list1176 1177 return music_features1178 1179###################################################################################1180 1181def Tegridy_Transform(chords_list, to_pitch=60, to_velocity=-1):1182 1183 '''Tegridy Transform1184 1185 Input: Flat chords list1186 Desired average pitch (-1 == no change)1187 Desired average velocity (-1 == no change)1188 1189 Output: Transformed flat chords list1190 1191 Project Los Angeles1192 Tegridy Code 2021'''1193 1194 transformed_chords_list = []1195 1196 chords_list.sort(reverse=False, key=lambda x: x[1])1197 1198 chords_list_features = Optimus_Signature(chords_list)[1]1199 1200 pitch_diff = int((chords_list_features[0] + chords_list_features[1] + chords_list_features[2]) / 3) - to_pitch