Upload TMIDIX.py
Browse files
TMIDIX.py
CHANGED
@@ -9381,6 +9381,361 @@ def advanced_add_drums_to_escore_notes(escore_notes,
|
|
9381 |
|
9382 |
return delta_score_to_abs_score(drums_score)
|
9383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9384 |
###################################################################################
|
9385 |
#
|
9386 |
# This is the end of the TMIDI X Python module
|
|
|
9381 |
|
9382 |
return delta_score_to_abs_score(drums_score)
|
9383 |
|
9384 |
+
###################################################################################
|
9385 |
+
|
9386 |
+
MIDI_TEXT_EVENTS = ['text_event',
|
9387 |
+
'copyright_text_event',
|
9388 |
+
'track_name',
|
9389 |
+
'instrument_name',
|
9390 |
+
'lyric',
|
9391 |
+
'marker',
|
9392 |
+
'cue_point',
|
9393 |
+
'text_event_08',
|
9394 |
+
'text_event_09',
|
9395 |
+
'text_event_0a',
|
9396 |
+
'text_event_0b',
|
9397 |
+
'text_event_0c',
|
9398 |
+
'text_event_0d',
|
9399 |
+
'text_event_0e',
|
9400 |
+
'text_event_0f'
|
9401 |
+
]
|
9402 |
+
|
9403 |
+
###################################################################################
|
9404 |
+
|
9405 |
+
import hashlib
|
9406 |
+
import re
|
9407 |
+
|
9408 |
+
###################################################################################
|
9409 |
+
|
9410 |
+
def get_md5_hash(data):
|
9411 |
+
return hashlib.md5(data).hexdigest()
|
9412 |
+
|
9413 |
+
###################################################################################
|
9414 |
+
|
9415 |
+
def is_valid_md5_hash(string):
|
9416 |
+
return bool(re.match(r'^[a-fA-F0-9]{32}$', string))
|
9417 |
+
|
9418 |
+
###################################################################################
|
9419 |
+
|
9420 |
+
def clean_string(original_string,
|
9421 |
+
regex=r'[^a-zA-Z0-9 ]',
|
9422 |
+
remove_duplicate_spaces=True,
|
9423 |
+
title=False
|
9424 |
+
):
|
9425 |
+
|
9426 |
+
cstr1 = re.sub(regex, '', original_string)
|
9427 |
+
|
9428 |
+
if title:
|
9429 |
+
cstr1 = cstr1.title()
|
9430 |
+
|
9431 |
+
if remove_duplicate_spaces:
|
9432 |
+
return re.sub(r'\s+', ' ', cstr1).strip()
|
9433 |
+
|
9434 |
+
else:
|
9435 |
+
return cstr1
|
9436 |
+
|
9437 |
+
###################################################################################
|
9438 |
+
|
9439 |
+
def encode_to_ord(text, chars_range=[], sub_char='', chars_shift=0):
|
9440 |
+
|
9441 |
+
if not chars_range:
|
9442 |
+
chars_range = [32] + list(range(65, 91)) + list(range(97, 123))
|
9443 |
+
|
9444 |
+
if sub_char:
|
9445 |
+
chars_range.append(ord(sub_char))
|
9446 |
+
|
9447 |
+
chars_range = sorted(set(chars_range))
|
9448 |
+
|
9449 |
+
encoded = []
|
9450 |
+
|
9451 |
+
for char in text:
|
9452 |
+
if ord(char) in chars_range:
|
9453 |
+
encoded.append(chars_range.index(ord(char)) + chars_shift)
|
9454 |
+
|
9455 |
+
else:
|
9456 |
+
if sub_char:
|
9457 |
+
encoded.append(chars_range.index(ord(sub_char)) + chars_shift)
|
9458 |
+
|
9459 |
+
|
9460 |
+
return [encoded, chars_range]
|
9461 |
+
|
9462 |
+
###################################################################################
|
9463 |
+
|
9464 |
+
def decode_from_ord(ord_list, chars_range=[], sub_char='', chars_shift=0):
|
9465 |
+
|
9466 |
+
if not chars_range:
|
9467 |
+
chars_range = [32] + list(range(65, 91)) + list(range(97, 123))
|
9468 |
+
|
9469 |
+
if sub_char:
|
9470 |
+
chars_range.append(ord(sub_char))
|
9471 |
+
|
9472 |
+
chars_range = sorted(set(chars_range))
|
9473 |
+
|
9474 |
+
return ''.join(chr(chars_range[num-chars_shift]) if 0 <= num-chars_shift < len(chars_range) else sub_char for num in ord_list)
|
9475 |
+
|
9476 |
+
###################################################################################
|
9477 |
+
|
9478 |
+
def lists_similarity(list1, list2, by_elements=True, by_sum=True):
|
9479 |
+
|
9480 |
+
if len(list1) != len(list2):
|
9481 |
+
return -1
|
9482 |
+
|
9483 |
+
element_ratios = []
|
9484 |
+
total_counts1 = sum(list1)
|
9485 |
+
total_counts2 = sum(list2)
|
9486 |
+
|
9487 |
+
for a, b in zip(list1, list2):
|
9488 |
+
if a == 0 and b == 0:
|
9489 |
+
element_ratios.append(1)
|
9490 |
+
elif a == 0 or b == 0:
|
9491 |
+
element_ratios.append(0)
|
9492 |
+
else:
|
9493 |
+
element_ratios.append(min(a, b) / max(a, b))
|
9494 |
+
|
9495 |
+
average_element_ratio = sum(element_ratios) / len(element_ratios)
|
9496 |
+
|
9497 |
+
total_counts_ratio = min(total_counts1, total_counts2) / max(total_counts1, total_counts2)
|
9498 |
+
|
9499 |
+
if by_elements and by_sum:
|
9500 |
+
return (average_element_ratio + total_counts_ratio) / 2
|
9501 |
+
|
9502 |
+
elif by_elements and not by_sum:
|
9503 |
+
return average_element_ratio
|
9504 |
+
|
9505 |
+
elif not by_elements and by_sum:
|
9506 |
+
return total_counts_ratio
|
9507 |
+
|
9508 |
+
else:
|
9509 |
+
return -1
|
9510 |
+
|
9511 |
+
###################################################################################
|
9512 |
+
|
9513 |
+
def find_indexes(lst, value, mode='equal', dual_mode=True):
|
9514 |
+
|
9515 |
+
indexes = []
|
9516 |
+
|
9517 |
+
if mode == 'equal' or dual_mode:
|
9518 |
+
indexes.extend([index for index, elem in enumerate(lst) if elem == value])
|
9519 |
+
|
9520 |
+
if mode == 'smaller':
|
9521 |
+
indexes.extend([index for index, elem in enumerate(lst) if elem < value])
|
9522 |
+
|
9523 |
+
if mode == 'larger':
|
9524 |
+
indexes.extend([index for index, elem in enumerate(lst) if elem > value])
|
9525 |
+
|
9526 |
+
return sorted(set(indexes))
|
9527 |
+
|
9528 |
+
###################################################################################
|
9529 |
+
|
9530 |
+
NUMERALS = ["one", "two", "three", "four",
|
9531 |
+
"five", "six", "seven", "eight",
|
9532 |
+
"nine", "ten", "eleven", "twelve",
|
9533 |
+
"thirteen", "fourteen", "fifteen", "sixteen"
|
9534 |
+
]
|
9535 |
+
|
9536 |
+
SEMITONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
|
9537 |
+
|
9538 |
+
MOOD_SCALES = ['Major', 'Minor', 'Major Minor']
|
9539 |
+
|
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'
|
9555 |
+
|
9556 |
+
elif 1.5 <= song_time_min < 2.5:
|
9557 |
+
song_length = 'average'
|
9558 |
+
|
9559 |
+
elif song_time_min >= 2.5:
|
9560 |
+
song_length = 'long'
|
9561 |
+
|
9562 |
+
#==============================================================================
|
9563 |
+
|
9564 |
+
escore_times = [e[1] for e in escore_notes if e[3] != 9]
|
9565 |
+
|
9566 |
+
comp_type = ''
|
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 |
+
|
9590 |
+
else:
|
9591 |
+
drums_present = False
|
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 |
+
|
9599 |
+
pitches = [e[4] for e in escore_notes if e[3] != 9]
|
9600 |
+
|
9601 |
+
key = ''
|
9602 |
+
|
9603 |
+
if pitches:
|
9604 |
+
key = SEMITONES[statistics.mode(pitches) % 12]
|
9605 |
+
|
9606 |
+
#==============================================================================
|
9607 |
+
|
9608 |
+
mood = ''
|
9609 |
+
|
9610 |
+
if pitches:
|
9611 |
+
|
9612 |
+
cscore = chordify_score([1000, escore_notes])
|
9613 |
+
|
9614 |
+
tones_chords = Counter()
|
9615 |
+
|
9616 |
+
for c in cscore:
|
9617 |
+
if len([e for e in c if e[3] != 9]) > 0:
|
9618 |
+
tones_chords[tuple(sorted(set([e[4] % 12 for e in c if e[3] != 9])))] += 1
|
9619 |
+
|
9620 |
+
most_common_tones_chords = [check_and_fix_tones_chord(list(c[0])) for c in tones_chords.most_common(10)]
|
9621 |
+
|
9622 |
+
mood_scale = statistics.mode(tones_chords_to_types(most_common_tones_chords, return_chord_type_index=True)) % 3
|
9623 |
+
|
9624 |
+
mood = MOOD_SCALES[mood_scale]
|
9625 |
+
|
9626 |
+
#==============================================================================
|
9627 |
+
|
9628 |
+
if pitches:
|
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:
|
9651 |
+
tone = 'bass'
|
9652 |
+
|
9653 |
+
elif 50 <= escore_averages[2] <= 70:
|
9654 |
+
tone = 'midrange'
|
9655 |
+
|
9656 |
+
elif escore_averages[2] > 70:
|
9657 |
+
tone = 'treble'
|
9658 |
+
|
9659 |
+
if escore_averages[3] < 80:
|
9660 |
+
dynamics = 'quiet'
|
9661 |
+
|
9662 |
+
elif 80 <= escore_averages[3] <= 100:
|
9663 |
+
dynamics = 'average'
|
9664 |
+
|
9665 |
+
elif escore_averages[3] > 100:
|
9666 |
+
dynamics = 'loud'
|
9667 |
+
|
9668 |
+
#==============================================================================
|
9669 |
+
|
9670 |
+
description = ''
|
9671 |
+
|
9672 |
+
if song_name != '':
|
9673 |
+
description = 'Song "' + song_name + '"'
|
9674 |
+
|
9675 |
+
if artist_name != '':
|
9676 |
+
description += ' by ' + artist_name
|
9677 |
+
|
9678 |
+
if song_name != '' or artist_name != '':
|
9679 |
+
description += '.'
|
9680 |
+
description += '\n'
|
9681 |
+
|
9682 |
+
description += 'The song is '
|
9683 |
+
|
9684 |
+
if song_length != 'average':
|
9685 |
+
description += 'a ' + song_length
|
9686 |
+
|
9687 |
+
else:
|
9688 |
+
description += 'an ' + song_length
|
9689 |
+
|
9690 |
+
description += ' duration '
|
9691 |
+
|
9692 |
+
description += comp_type + ' composition'
|
9693 |
+
|
9694 |
+
if comp_type != 'drums only':
|
9695 |
+
|
9696 |
+
if drums_present:
|
9697 |
+
description += ' with drums'
|
9698 |
+
|
9699 |
+
else:
|
9700 |
+
description += ' without drums'
|
9701 |
+
|
9702 |
+
if key and mood:
|
9703 |
+
description += ' in ' + key + ' ' + mood
|
9704 |
+
|
9705 |
+
description += '.'
|
9706 |
+
|
9707 |
+
description += '\n'
|
9708 |
+
|
9709 |
+
if pitches:
|
9710 |
+
|
9711 |
+
description += 'It has '
|
9712 |
+
|
9713 |
+
description += rythm + ' rythm, '
|
9714 |
+
description += tempo + ' tempo, '
|
9715 |
+
description += tone + ' tone and '
|
9716 |
+
description += dynamics + ' dynamics.'
|
9717 |
+
|
9718 |
+
description += '\n'
|
9719 |
+
|
9720 |
+
description += 'The song '
|
9721 |
+
|
9722 |
+
if len(instruments) == 1:
|
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
|