asigalov61 commited on
Commit
7fd3ee0
·
verified ·
1 Parent(s): eb19d41

Upload TMIDIX.py

Browse files
Files changed (1) hide show
  1. TMIDIX.py +1055 -11
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.
@@ -4690,7 +4690,8 @@ def augment_enhanced_score_notes(enhanced_score_notes,
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)
@@ -4740,6 +4741,9 @@ def augment_enhanced_score_notes(enhanced_score_notes,
4740
  esn.sort(key=lambda x: x[6])
4741
  esn.sort(key=lambda x: x[4], reverse=True)
4742
  esn.sort(key=lambda x: x[1])
 
 
 
4743
 
4744
  return esn
4745
 
@@ -7097,13 +7101,25 @@ def escore_notes_averages(escore_notes,
7097
  else:
7098
  durs = [e[durs_index] for e in escore_notes if e[chans_index] != 9]
7099
 
 
 
 
 
 
 
7100
  if return_ptcs_and_vels:
7101
  if average_drums:
7102
  ptcs = [e[ptcs_index] for e in escore_notes]
7103
  vels = [e[vels_index] for e in escore_notes]
7104
  else:
7105
  ptcs = [e[ptcs_index] for e in escore_notes if e[chans_index] != 9]
7106
- vels = [e[vels_index] for e in escore_notes if e[chans_index] != 9]
 
 
 
 
 
 
7107
 
7108
  return [sum(times) / len(times), sum(durs) / len(durs), sum(ptcs) / len(ptcs), sum(vels) / len(vels)]
7109
 
@@ -7883,19 +7899,21 @@ def solo_piano_escore_notes(escore_notes,
7883
  chord = []
7884
 
7885
  for cc in c:
7886
- if cc[pitches_index] not in seen:
7887
 
7888
- if cc[channels_index] != 9:
 
 
7889
  cc[channels_index] = 0
7890
  cc[patches_index] = 0
7891
-
7892
  chord.append(cc)
7893
  seen.append(cc[pitches_index])
7894
-
7895
- else:
7896
- if keep_drums:
 
7897
  chord.append(cc)
7898
- seen.append(cc[pitches_index])
7899
 
7900
  sp_escore_notes.append(chord)
7901
 
@@ -9254,6 +9272,1032 @@ def find_highest_density_escore_notes_chunk(escore_notes, max_chunk_time=512):
9254
 
9255
  return chunk_escore
9256
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9257
  ###################################################################################
9258
  #
9259
  # 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.
 
4690
  pitch_shift=0,
4691
  ceil_timings=False,
4692
  round_timings=False,
4693
+ legacy_timings=True,
4694
+ sort_drums_last=False
4695
  ):
4696
 
4697
  esn = copy.deepcopy(enhanced_score_notes)
 
4741
  esn.sort(key=lambda x: x[6])
4742
  esn.sort(key=lambda x: x[4], reverse=True)
4743
  esn.sort(key=lambda x: x[1])
4744
+
4745
+ if sort_drums_last:
4746
+ esn.sort(key=lambda x: (x[1], -x[4], x[6]) if x[6] != 128 else (x[1], x[6], -x[4]))
4747
 
4748
  return esn
4749
 
 
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
 
 
7899
  chord = []
7900
 
7901
  for cc in c:
 
7902
 
7903
+ if cc[channels_index] != 9:
7904
+ if cc[pitches_index] not in seen:
7905
+
7906
  cc[channels_index] = 0
7907
  cc[patches_index] = 0
7908
+
7909
  chord.append(cc)
7910
  seen.append(cc[pitches_index])
7911
+
7912
+ else:
7913
+ if keep_drums:
7914
+ if cc[pitches_index]+128 not in seen:
7915
  chord.append(cc)
7916
+ seen.append(cc[pitches_index]+128)
7917
 
7918
  sp_escore_notes.append(chord)
7919
 
 
9272
 
9273
  return chunk_escore
9274
 
9275
+ ###################################################################################
9276
+
9277
+ def advanced_add_drums_to_escore_notes(escore_notes,
9278
+ main_beat_min_dtime=5,
9279
+ main_beat_dtime_thres=1,
9280
+ drums_durations_value=2,
9281
+ drums_pitches_velocities=[(36, 100),
9282
+ (38, 100),
9283
+ (41, 125)],
9284
+ recalculate_score_timings=True,
9285
+ intro_drums_count=4,
9286
+ intro_drums_time_k=4,
9287
+ intro_drums_pitch_velocity=[37, 110]
9288
+ ):
9289
+
9290
+ #===========================================================
9291
+
9292
+ new_dscore = delta_score_notes(escore_notes)
9293
+
9294
+ times = [d[1] for d in new_dscore if d[1] != 0]
9295
+
9296
+ time = [c[0] for c in Counter(times).most_common() if c[0] >= main_beat_min_dtime][0]
9297
+
9298
+ #===========================================================
9299
+
9300
+ if intro_drums_count > 0:
9301
+
9302
+ drums_score = []
9303
+
9304
+ for i in range(intro_drums_count):
9305
+
9306
+ if i == 0:
9307
+ dtime = 0
9308
+
9309
+ else:
9310
+ dtime = time
9311
+
9312
+ drums_score.append(['note',
9313
+ dtime * intro_drums_time_k,
9314
+ drums_durations_value,
9315
+ 9,
9316
+ intro_drums_pitch_velocity[0],
9317
+ intro_drums_pitch_velocity[1],
9318
+ 128]
9319
+ )
9320
+
9321
+ new_dscore[0][1] = time * intro_drums_time_k
9322
+
9323
+ new_dscore = drums_score + new_dscore
9324
+
9325
+ #===========================================================
9326
+
9327
+ for e in new_dscore:
9328
+
9329
+ if abs(e[1] - time) == main_beat_dtime_thres:
9330
+ e[1] = time
9331
+
9332
+ if recalculate_score_timings:
9333
+
9334
+ if e[1] % time != 0 and e[1] > time:
9335
+ if e[1] % time < time // 2:
9336
+ e[1] -= e[1] % time
9337
+
9338
+ else:
9339
+ e[1] += time - (e[1] % time)
9340
+
9341
+ #===========================================================
9342
+
9343
+ drums_score = []
9344
+
9345
+ dtime = 0
9346
+
9347
+ idx = 0
9348
+
9349
+ for i, e in enumerate(new_dscore):
9350
+
9351
+ drums_score.append(e)
9352
+
9353
+ dtime += e[1]
9354
+
9355
+ if e[1] != 0:
9356
+ idx += 1
9357
+
9358
+ if i >= intro_drums_count:
9359
+
9360
+ if (e[1] % time == 0 and e[1] != 0) or i == 0:
9361
+
9362
+ if idx % 2 == 0 and e[1] != 0:
9363
+ drums_score.append(['note',
9364
+ 0,
9365
+ drums_durations_value,
9366
+ 9,
9367
+ drums_pitches_velocities[0][0],
9368
+ drums_pitches_velocities[0][1],
9369
+ 128]
9370
+ )
9371
+
9372
+ if idx % 2 != 0 and e[1] != 0:
9373
+ drums_score.append(['note',
9374
+ 0,
9375
+ drums_durations_value,
9376
+ 9,
9377
+ drums_pitches_velocities[1][0],
9378
+ drums_pitches_velocities[1][1],
9379
+ 128]
9380
+ )
9381
+
9382
+ if idx % 4 == 0 and e[1] != 0:
9383
+ drums_score.append(['note',
9384
+ 0,
9385
+ drums_durations_value,
9386
+ 9,
9387
+ drums_pitches_velocities[2][0],
9388
+ drums_pitches_velocities[2][1],
9389
+ 128]
9390
+ )
9391
+
9392
+ #===========================================================
9393
+
9394
+ return delta_score_to_abs_score(drums_score)
9395
+
9396
+ ###################################################################################
9397
+
9398
+ MIDI_TEXT_EVENTS = ['text_event',
9399
+ 'copyright_text_event',
9400
+ 'track_name',
9401
+ 'instrument_name',
9402
+ 'lyric',
9403
+ 'marker',
9404
+ 'cue_point',
9405
+ 'text_event_08',
9406
+ 'text_event_09',
9407
+ 'text_event_0a',
9408
+ 'text_event_0b',
9409
+ 'text_event_0c',
9410
+ 'text_event_0d',
9411
+ 'text_event_0e',
9412
+ 'text_event_0f'
9413
+ ]
9414
+
9415
+ ###################################################################################
9416
+
9417
+ import hashlib
9418
+ import re
9419
+
9420
+ ###################################################################################
9421
+
9422
+ def get_md5_hash(data):
9423
+ return hashlib.md5(data).hexdigest()
9424
+
9425
+ ###################################################################################
9426
+
9427
+ def is_valid_md5_hash(string):
9428
+ return bool(re.match(r'^[a-fA-F0-9]{32}$', string))
9429
+
9430
+ ###################################################################################
9431
+
9432
+ def clean_string(original_string,
9433
+ regex=r'[^a-zA-Z0-9 ]',
9434
+ remove_duplicate_spaces=True,
9435
+ title=False
9436
+ ):
9437
+
9438
+ cstr1 = re.sub(regex, '', original_string)
9439
+
9440
+ if title:
9441
+ cstr1 = cstr1.title()
9442
+
9443
+ if remove_duplicate_spaces:
9444
+ return re.sub(r'\s+', ' ', cstr1).strip()
9445
+
9446
+ else:
9447
+ return cstr1
9448
+
9449
+ ###################################################################################
9450
+
9451
+ def encode_to_ord(text, chars_range=[], sub_char='', chars_shift=0):
9452
+
9453
+ if not chars_range:
9454
+ chars_range = [32] + list(range(65, 91)) + list(range(97, 123))
9455
+
9456
+ if sub_char:
9457
+ chars_range.append(ord(sub_char))
9458
+
9459
+ chars_range = sorted(set(chars_range))
9460
+
9461
+ encoded = []
9462
+
9463
+ for char in text:
9464
+ if ord(char) in chars_range:
9465
+ encoded.append(chars_range.index(ord(char)) + chars_shift)
9466
+
9467
+ else:
9468
+ if sub_char:
9469
+ encoded.append(chars_range.index(ord(sub_char)) + chars_shift)
9470
+
9471
+
9472
+ return [encoded, chars_range]
9473
+
9474
+ ###################################################################################
9475
+
9476
+ def decode_from_ord(ord_list, chars_range=[], sub_char='', chars_shift=0):
9477
+
9478
+ if not chars_range:
9479
+ chars_range = [32] + list(range(65, 91)) + list(range(97, 123))
9480
+
9481
+ if sub_char:
9482
+ chars_range.append(ord(sub_char))
9483
+
9484
+ chars_range = sorted(set(chars_range))
9485
+
9486
+ return ''.join(chr(chars_range[num-chars_shift]) if 0 <= num-chars_shift < len(chars_range) else sub_char for num in ord_list)
9487
+
9488
+ ###################################################################################
9489
+
9490
+ def lists_similarity(list1, list2, by_elements=True, by_sum=True):
9491
+
9492
+ if len(list1) != len(list2):
9493
+ return -1
9494
+
9495
+ element_ratios = []
9496
+ total_counts1 = sum(list1)
9497
+ total_counts2 = sum(list2)
9498
+
9499
+ for a, b in zip(list1, list2):
9500
+ if a == 0 and b == 0:
9501
+ element_ratios.append(1)
9502
+ elif a == 0 or b == 0:
9503
+ element_ratios.append(0)
9504
+ else:
9505
+ element_ratios.append(min(a, b) / max(a, b))
9506
+
9507
+ average_element_ratio = sum(element_ratios) / len(element_ratios)
9508
+
9509
+ total_counts_ratio = min(total_counts1, total_counts2) / max(total_counts1, total_counts2)
9510
+
9511
+ if by_elements and by_sum:
9512
+ return (average_element_ratio + total_counts_ratio) / 2
9513
+
9514
+ elif by_elements and not by_sum:
9515
+ return average_element_ratio
9516
+
9517
+ elif not by_elements and by_sum:
9518
+ return total_counts_ratio
9519
+
9520
+ else:
9521
+ return -1
9522
+
9523
+ ###################################################################################
9524
+
9525
+ def find_indexes(lst, value, mode='equal', dual_mode=True):
9526
+
9527
+ indexes = []
9528
+
9529
+ if mode == 'equal' or dual_mode:
9530
+ indexes.extend([index for index, elem in enumerate(lst) if elem == value])
9531
+
9532
+ if mode == 'smaller':
9533
+ indexes.extend([index for index, elem in enumerate(lst) if elem < value])
9534
+
9535
+ if mode == 'larger':
9536
+ indexes.extend([index for index, elem in enumerate(lst) if elem > value])
9537
+
9538
+ return sorted(set(indexes))
9539
+
9540
+ ###################################################################################
9541
+
9542
+ NUMERALS = ["one", "two", "three", "four",
9543
+ "five", "six", "seven", "eight",
9544
+ "nine", "ten", "eleven", "twelve",
9545
+ "thirteen", "fourteen", "fifteen", "sixteen"
9546
+ ]
9547
+
9548
+ SEMITONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
9549
+
9550
+ MOOD_SCALES = ['Major', 'Minor', 'Major Minor']
9551
+
9552
+ ###################################################################################
9553
+
9554
+ def alpha_str(string):
9555
+ return re.sub(r'[^a-zA-Z ()]', '', string).strip()
9556
+
9557
+ ###################################################################################
9558
+
9559
+ def escore_notes_to_text_description(escore_notes, song_name='', artist_name=''):
9560
+
9561
+ #==============================================================================
9562
+
9563
+ song_time_min = (escore_notes[-1][1] * 16) / 1000 / 60
9564
+
9565
+ if song_time_min < 1.5:
9566
+ song_length = 'short'
9567
+
9568
+ elif 1.5 <= song_time_min < 2.5:
9569
+ song_length = 'average'
9570
+
9571
+ elif song_time_min >= 2.5:
9572
+ song_length = 'long'
9573
+
9574
+ #==============================================================================
9575
+
9576
+ escore_times = [e[1] for e in escore_notes if e[3] != 9]
9577
+
9578
+ comp_type = ''
9579
+
9580
+ if len(escore_times) > 0:
9581
+ if len(escore_times) == len(set(escore_times)):
9582
+ comp_type = 'monophonic melody'
9583
+
9584
+ elif len(escore_times) >= len(set(escore_times)) and 1 in Counter(escore_times).values():
9585
+ comp_type = 'melody and accompaniment'
9586
+
9587
+ elif len(escore_times) >= len(set(escore_times)) and 1 not in Counter(escore_times).values():
9588
+ comp_type = 'accompaniment'
9589
+
9590
+ else:
9591
+ comp_type = 'drums only'
9592
+
9593
+ #==============================================================================
9594
+
9595
+ patches = sorted(set([e[6] for e in escore_notes]))
9596
+
9597
+ instruments = [alpha_str(Number2patch[p]) for p in patches if p < 128]
9598
+
9599
+ if 128 in patches:
9600
+ drums_present = True
9601
+
9602
+ else:
9603
+ drums_present = False
9604
+
9605
+ drums_pitches = [e[4] for e in escore_notes if e[3] == 9]
9606
+
9607
+ most_common_drums = [alpha_str(Notenum2percussion[p[0]]) for p in Counter(drums_pitches).most_common(3) if p[0] in Notenum2percussion]
9608
+
9609
+ #==============================================================================
9610
+
9611
+ pitches = [e[4] for e in escore_notes if e[3] != 9]
9612
+
9613
+ key = ''
9614
+
9615
+ if pitches:
9616
+ key = SEMITONES[statistics.mode(pitches) % 12]
9617
+
9618
+ #==============================================================================
9619
+
9620
+ mood = ''
9621
+
9622
+ if pitches:
9623
+
9624
+ cscore = chordify_score([1000, escore_notes])
9625
+
9626
+ tones_chords = Counter()
9627
+
9628
+ for c in cscore:
9629
+ if len([e for e in c if e[3] != 9]) > 0:
9630
+ tones_chords[tuple(sorted(set([e[4] % 12 for e in c if e[3] != 9])))] += 1
9631
+
9632
+ most_common_tones_chords = [check_and_fix_tones_chord(list(c[0])) for c in tones_chords.most_common(10)]
9633
+
9634
+ mood_scale = statistics.mode(tones_chords_to_types(most_common_tones_chords, return_chord_type_index=True)) % 3
9635
+
9636
+ mood = MOOD_SCALES[mood_scale]
9637
+
9638
+ #==============================================================================
9639
+
9640
+ if pitches:
9641
+
9642
+ escore_averages = escore_notes_averages(escore_notes, return_ptcs_and_vels=True)
9643
+
9644
+ if escore_averages[0] < 8:
9645
+ rythm = 'fast'
9646
+
9647
+ elif 8 <= escore_averages[0] <= 12:
9648
+ rythm = 'average'
9649
+
9650
+ elif escore_averages[0] > 12:
9651
+ rythm = 'slow'
9652
+
9653
+ if escore_averages[1] < 16:
9654
+ tempo = 'fast'
9655
+
9656
+ elif 16 <= escore_averages[1] <= 24:
9657
+ tempo = 'average'
9658
+
9659
+ elif escore_averages[1] > 24:
9660
+ tempo = 'slow'
9661
+
9662
+ if escore_averages[2] < 50:
9663
+ tone = 'bass'
9664
+
9665
+ elif 50 <= escore_averages[2] <= 70:
9666
+ tone = 'midrange'
9667
+
9668
+ elif escore_averages[2] > 70:
9669
+ tone = 'treble'
9670
+
9671
+ if escore_averages[3] < 80:
9672
+ dynamics = 'quiet'
9673
+
9674
+ elif 80 <= escore_averages[3] <= 100:
9675
+ dynamics = 'average'
9676
+
9677
+ elif escore_averages[3] > 100:
9678
+ dynamics = 'loud'
9679
+
9680
+ #==============================================================================
9681
+
9682
+ description = ''
9683
+
9684
+ if song_name != '':
9685
+ description = 'Song "' + song_name + '"'
9686
+
9687
+ if artist_name != '':
9688
+ description += ' by ' + artist_name
9689
+
9690
+ if song_name != '' or artist_name != '':
9691
+ description += '.'
9692
+ description += '\n'
9693
+
9694
+ description += 'The song is '
9695
+
9696
+ if song_length != 'average':
9697
+ description += 'a ' + song_length
9698
+
9699
+ else:
9700
+ description += 'an ' + song_length
9701
+
9702
+ description += ' duration '
9703
+
9704
+ description += comp_type + ' composition'
9705
+
9706
+ if comp_type != 'drum track':
9707
+
9708
+ if drums_present:
9709
+ description += ' with drums'
9710
+
9711
+ else:
9712
+ description += ' without drums'
9713
+
9714
+ if key and mood:
9715
+ description += ' in ' + key + ' ' + mood
9716
+
9717
+ description += '.'
9718
+
9719
+ description += '\n'
9720
+
9721
+ if pitches:
9722
+
9723
+ description += 'It has '
9724
+
9725
+ description += rythm + ' rythm, '
9726
+ description += tempo + ' tempo, '
9727
+ description += tone + ' tone and '
9728
+ description += dynamics + ' dynamics.'
9729
+
9730
+ description += '\n'
9731
+
9732
+ description += 'The song '
9733
+
9734
+ if len(instruments) == 1:
9735
+ description += 'is played on a solo ' + instruments[0] + '.'
9736
+
9737
+ else:
9738
+ description += 'features ' + NUMERALS[max(0, min(15, len(instruments)-1))] + ' instruments: '
9739
+ description += ', '.join(instruments[:-1]) + ' and ' + instruments[-1] + '.'
9740
+
9741
+ description += '\n'
9742
+
9743
+ if drums_present and most_common_drums:
9744
+ description += 'The drum track has predominant '
9745
+ description += ', '.join(most_common_drums[:-1]) + ' and ' + most_common_drums[-1] + '.'
9746
+
9747
+ #==============================================================================
9748
+
9749
+ return description
9750
+
9751
+ ###################################################################################
9752
+
9753
+ #==================================================================================
9754
+ #
9755
+ # Below constants code is a courtesy of MidiTok
9756
+ #
9757
+ # Retrieved on 12/29/2024
9758
+ #
9759
+ # https://github.com/Natooz/MidiTok/blob/main/src/miditok/constants.py
9760
+ #
9761
+ #==================================================================================
9762
+
9763
+ MIDI_FILES_EXTENSIONS = [".mid", ".midi", ".kar", ".MID", ".MIDI", ".KAR"]
9764
+
9765
+ # The recommended pitches for piano in the GM2 specs are from 21 to 108
9766
+ PIANO_PITCH_RANGE = range(21, 109)
9767
+
9768
+ # Chord params
9769
+ # "chord_unknown" specifies the range of number of notes that can form "unknown" chords
9770
+ # (that do not fit in "chord_maps") to add in tokens.
9771
+ # Known chord maps, with 0 as root note
9772
+ BASIC_CHORDS_MAP = {
9773
+ "min": (0, 3, 7),
9774
+ "maj": (0, 4, 7),
9775
+ "dim": (0, 3, 6),
9776
+ "aug": (0, 4, 8),
9777
+ "sus2": (0, 2, 7),
9778
+ "sus4": (0, 5, 7),
9779
+ "7dom": (0, 4, 7, 10),
9780
+ "7min": (0, 3, 7, 10),
9781
+ "7maj": (0, 4, 7, 11),
9782
+ "7halfdim": (0, 3, 6, 10),
9783
+ "7dim": (0, 3, 6, 9),
9784
+ "7aug": (0, 4, 8, 11),
9785
+ "9maj": (0, 4, 7, 10, 14),
9786
+ "9min": (0, 4, 7, 10, 13),
9787
+ }
9788
+
9789
+ # Drums
9790
+ # Recommended range from the GM2 specs
9791
+ DRUMS_PITCH_RANGE = range(27, 90)
9792
+
9793
+ # Used with chords
9794
+ PITCH_CLASSES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
9795
+
9796
+ # http://newt.phys.unsw.edu.au/jw/notes.html
9797
+ # https://www.midi.org/specifications
9798
+
9799
+ # index i = program i+1 in the GM2 specs (7. Appendix A)
9800
+ # index i = program i as retrieved by packages
9801
+ MIDI_INSTRUMENTS = [
9802
+ # Piano
9803
+ {"name": "Acoustic Grand Piano", "pitch_range": range(21, 109)},
9804
+ {"name": "Bright Acoustic Piano", "pitch_range": range(21, 109)},
9805
+ {"name": "Electric Grand Piano", "pitch_range": range(21, 109)},
9806
+ {"name": "Honky-tonk Piano", "pitch_range": range(21, 109)},
9807
+ {"name": "Electric Piano 1", "pitch_range": range(28, 104)},
9808
+ {"name": "Electric Piano 2", "pitch_range": range(28, 104)},
9809
+ {"name": "Harpsichord", "pitch_range": range(41, 90)},
9810
+ {"name": "Clavi", "pitch_range": range(36, 97)},
9811
+ # Chromatic Percussion
9812
+ {"name": "Celesta", "pitch_range": range(60, 109)},
9813
+ {"name": "Glockenspiel", "pitch_range": range(72, 109)},
9814
+ {"name": "Music Box", "pitch_range": range(60, 85)},
9815
+ {"name": "Vibraphone", "pitch_range": range(53, 90)},
9816
+ {"name": "Marimba", "pitch_range": range(48, 85)},
9817
+ {"name": "Xylophone", "pitch_range": range(65, 97)},
9818
+ {"name": "Tubular Bells", "pitch_range": range(60, 78)},
9819
+ {"name": "Dulcimer", "pitch_range": range(60, 85)},
9820
+ # Organs
9821
+ {"name": "Drawbar Organ", "pitch_range": range(36, 97)},
9822
+ {"name": "Percussive Organ", "pitch_range": range(36, 97)},
9823
+ {"name": "Rock Organ", "pitch_range": range(36, 97)},
9824
+ {"name": "Church Organ", "pitch_range": range(21, 109)},
9825
+ {"name": "Reed Organ", "pitch_range": range(36, 97)},
9826
+ {"name": "Accordion", "pitch_range": range(53, 90)},
9827
+ {"name": "Harmonica", "pitch_range": range(60, 85)},
9828
+ {"name": "Tango Accordion", "pitch_range": range(53, 90)},
9829
+ # Guitars
9830
+ {"name": "Acoustic Guitar (nylon)", "pitch_range": range(40, 85)},
9831
+ {"name": "Acoustic Guitar (steel)", "pitch_range": range(40, 85)},
9832
+ {"name": "Electric Guitar (jazz)", "pitch_range": range(40, 87)},
9833
+ {"name": "Electric Guitar (clean)", "pitch_range": range(40, 87)},
9834
+ {"name": "Electric Guitar (muted)", "pitch_range": range(40, 87)},
9835
+ {"name": "Overdriven Guitar", "pitch_range": range(40, 87)},
9836
+ {"name": "Distortion Guitar", "pitch_range": range(40, 87)},
9837
+ {"name": "Guitar Harmonics", "pitch_range": range(40, 87)},
9838
+ # Bass
9839
+ {"name": "Acoustic Bass", "pitch_range": range(28, 56)},
9840
+ {"name": "Electric Bass (finger)", "pitch_range": range(28, 56)},
9841
+ {"name": "Electric Bass (pick)", "pitch_range": range(28, 56)},
9842
+ {"name": "Fretless Bass", "pitch_range": range(28, 56)},
9843
+ {"name": "Slap Bass 1", "pitch_range": range(28, 56)},
9844
+ {"name": "Slap Bass 2", "pitch_range": range(28, 56)},
9845
+ {"name": "Synth Bass 1", "pitch_range": range(28, 56)},
9846
+ {"name": "Synth Bass 2", "pitch_range": range(28, 56)},
9847
+ # Strings & Orchestral instruments
9848
+ {"name": "Violin", "pitch_range": range(55, 94)},
9849
+ {"name": "Viola", "pitch_range": range(48, 85)},
9850
+ {"name": "Cello", "pitch_range": range(36, 73)},
9851
+ {"name": "Contrabass", "pitch_range": range(28, 56)},
9852
+ {"name": "Tremolo Strings", "pitch_range": range(28, 94)},
9853
+ {"name": "Pizzicato Strings", "pitch_range": range(28, 94)},
9854
+ {"name": "Orchestral Harp", "pitch_range": range(23, 104)},
9855
+ {"name": "Timpani", "pitch_range": range(36, 58)},
9856
+ # Ensembles
9857
+ {"name": "String Ensembles 1", "pitch_range": range(28, 97)},
9858
+ {"name": "String Ensembles 2", "pitch_range": range(28, 97)},
9859
+ {"name": "SynthStrings 1", "pitch_range": range(36, 97)},
9860
+ {"name": "SynthStrings 2", "pitch_range": range(36, 97)},
9861
+ {"name": "Choir Aahs", "pitch_range": range(48, 80)},
9862
+ {"name": "Voice Oohs", "pitch_range": range(48, 80)},
9863
+ {"name": "Synth Voice", "pitch_range": range(48, 85)},
9864
+ {"name": "Orchestra Hit", "pitch_range": range(48, 73)},
9865
+ # Brass
9866
+ {"name": "Trumpet", "pitch_range": range(58, 95)},
9867
+ {"name": "Trombone", "pitch_range": range(34, 76)},
9868
+ {"name": "Tuba", "pitch_range": range(29, 56)},
9869
+ {"name": "Muted Trumpet", "pitch_range": range(58, 83)},
9870
+ {"name": "French Horn", "pitch_range": range(41, 78)},
9871
+ {"name": "Brass Section", "pitch_range": range(36, 97)},
9872
+ {"name": "Synth Brass 1", "pitch_range": range(36, 97)},
9873
+ {"name": "Synth Brass 2", "pitch_range": range(36, 97)},
9874
+ # Reed
9875
+ {"name": "Soprano Sax", "pitch_range": range(54, 88)},
9876
+ {"name": "Alto Sax", "pitch_range": range(49, 81)},
9877
+ {"name": "Tenor Sax", "pitch_range": range(42, 76)},
9878
+ {"name": "Baritone Sax", "pitch_range": range(37, 69)},
9879
+ {"name": "Oboe", "pitch_range": range(58, 92)},
9880
+ {"name": "English Horn", "pitch_range": range(52, 82)},
9881
+ {"name": "Bassoon", "pitch_range": range(34, 73)},
9882
+ {"name": "Clarinet", "pitch_range": range(50, 92)},
9883
+ # Pipe
9884
+ {"name": "Piccolo", "pitch_range": range(74, 109)},
9885
+ {"name": "Flute", "pitch_range": range(60, 97)},
9886
+ {"name": "Recorder", "pitch_range": range(60, 97)},
9887
+ {"name": "Pan Flute", "pitch_range": range(60, 97)},
9888
+ {"name": "Blown Bottle", "pitch_range": range(60, 97)},
9889
+ {"name": "Shakuhachi", "pitch_range": range(55, 85)},
9890
+ {"name": "Whistle", "pitch_range": range(60, 97)},
9891
+ {"name": "Ocarina", "pitch_range": range(60, 85)},
9892
+ # Synth Lead
9893
+ {"name": "Lead 1 (square)", "pitch_range": range(21, 109)},
9894
+ {"name": "Lead 2 (sawtooth)", "pitch_range": range(21, 109)},
9895
+ {"name": "Lead 3 (calliope)", "pitch_range": range(36, 97)},
9896
+ {"name": "Lead 4 (chiff)", "pitch_range": range(36, 97)},
9897
+ {"name": "Lead 5 (charang)", "pitch_range": range(36, 97)},
9898
+ {"name": "Lead 6 (voice)", "pitch_range": range(36, 97)},
9899
+ {"name": "Lead 7 (fifths)", "pitch_range": range(36, 97)},
9900
+ {"name": "Lead 8 (bass + lead)", "pitch_range": range(21, 109)},
9901
+ # Synth Pad
9902
+ {"name": "Pad 1 (new age)", "pitch_range": range(36, 97)},
9903
+ {"name": "Pad 2 (warm)", "pitch_range": range(36, 97)},
9904
+ {"name": "Pad 3 (polysynth)", "pitch_range": range(36, 97)},
9905
+ {"name": "Pad 4 (choir)", "pitch_range": range(36, 97)},
9906
+ {"name": "Pad 5 (bowed)", "pitch_range": range(36, 97)},
9907
+ {"name": "Pad 6 (metallic)", "pitch_range": range(36, 97)},
9908
+ {"name": "Pad 7 (halo)", "pitch_range": range(36, 97)},
9909
+ {"name": "Pad 8 (sweep)", "pitch_range": range(36, 97)},
9910
+ # Synth SFX
9911
+ {"name": "FX 1 (rain)", "pitch_range": range(36, 97)},
9912
+ {"name": "FX 2 (soundtrack)", "pitch_range": range(36, 97)},
9913
+ {"name": "FX 3 (crystal)", "pitch_range": range(36, 97)},
9914
+ {"name": "FX 4 (atmosphere)", "pitch_range": range(36, 97)},
9915
+ {"name": "FX 5 (brightness)", "pitch_range": range(36, 97)},
9916
+ {"name": "FX 6 (goblins)", "pitch_range": range(36, 97)},
9917
+ {"name": "FX 7 (echoes)", "pitch_range": range(36, 97)},
9918
+ {"name": "FX 8 (sci-fi)", "pitch_range": range(36, 97)},
9919
+ # Ethnic Misc.
9920
+ {"name": "Sitar", "pitch_range": range(48, 78)},
9921
+ {"name": "Banjo", "pitch_range": range(48, 85)},
9922
+ {"name": "Shamisen", "pitch_range": range(50, 80)},
9923
+ {"name": "Koto", "pitch_range": range(55, 85)},
9924
+ {"name": "Kalimba", "pitch_range": range(48, 80)},
9925
+ {"name": "Bag pipe", "pitch_range": range(36, 78)},
9926
+ {"name": "Fiddle", "pitch_range": range(55, 97)},
9927
+ {"name": "Shanai", "pitch_range": range(48, 73)},
9928
+ # Percussive
9929
+ {"name": "Tinkle Bell", "pitch_range": range(72, 85)},
9930
+ {"name": "Agogo", "pitch_range": range(60, 73)},
9931
+ {"name": "Steel Drums", "pitch_range": range(52, 77)},
9932
+ {"name": "Woodblock", "pitch_range": range(128)},
9933
+ {"name": "Taiko Drum", "pitch_range": range(128)},
9934
+ {"name": "Melodic Tom", "pitch_range": range(128)},
9935
+ {"name": "Synth Drum", "pitch_range": range(128)},
9936
+ {"name": "Reverse Cymbal", "pitch_range": range(128)},
9937
+ # SFX
9938
+ {"name": "Guitar Fret Noise, Guitar Cutting Noise", "pitch_range": range(128)},
9939
+ {"name": "Breath Noise, Flute Key Click", "pitch_range": range(128)},
9940
+ {
9941
+ "name": "Seashore, Rain, Thunder, Wind, Stream, Bubbles",
9942
+ "pitch_range": range(128),
9943
+ },
9944
+ {"name": "Bird Tweet, Dog, Horse Gallop", "pitch_range": range(128)},
9945
+ {
9946
+ "name": "Telephone Ring, Door Creaking, Door, Scratch, Wind Chime",
9947
+ "pitch_range": range(128),
9948
+ },
9949
+ {"name": "Helicopter, Car Sounds", "pitch_range": range(128)},
9950
+ {
9951
+ "name": "Applause, Laughing, Screaming, Punch, Heart Beat, Footstep",
9952
+ "pitch_range": range(128),
9953
+ },
9954
+ {"name": "Gunshot, Machine Gun, Lasergun, Explosion", "pitch_range": range(128)},
9955
+ ]
9956
+
9957
+ INSTRUMENTS_CLASSES = [
9958
+ {"name": "Piano", "program_range": range(8)}, # 0
9959
+ {"name": "Chromatic Percussion", "program_range": range(8, 16)},
9960
+ {"name": "Organ", "program_range": range(16, 24)},
9961
+ {"name": "Guitar", "program_range": range(24, 32)},
9962
+ {"name": "Bass", "program_range": range(32, 40)},
9963
+ {"name": "Strings", "program_range": range(40, 48)}, # 5
9964
+ {"name": "Ensemble", "program_range": range(48, 56)},
9965
+ {"name": "Brass", "program_range": range(56, 64)},
9966
+ {"name": "Reed", "program_range": range(64, 72)},
9967
+ {"name": "Pipe", "program_range": range(72, 80)},
9968
+ {"name": "Synth Lead", "program_range": range(80, 88)}, # 10
9969
+ {"name": "Synth Pad", "program_range": range(88, 96)},
9970
+ {"name": "Synth Effects", "program_range": range(96, 104)},
9971
+ {"name": "Ethnic", "program_range": range(104, 112)},
9972
+ {"name": "Percussive", "program_range": range(112, 120)},
9973
+ {"name": "Sound Effects", "program_range": range(120, 128)}, # 15
9974
+ {"name": "Drums", "program_range": range(-1, 0)},
9975
+ ]
9976
+
9977
+ # To easily get the class index of any instrument program
9978
+ CLASS_OF_INST = [
9979
+ i
9980
+ for i, inst_class in enumerate(INSTRUMENTS_CLASSES)
9981
+ for _ in inst_class["program_range"]
9982
+ ]
9983
+
9984
+ # index i = program i+1 in the GM2 specs (8. Appendix B)
9985
+ # index i = program i retrieved by packages
9986
+ DRUMS_SETS = {
9987
+ 0: "Standard",
9988
+ 8: "Room",
9989
+ 16: "Power",
9990
+ 24: "Electronic",
9991
+ 25: "Analog",
9992
+ 32: "Jazz",
9993
+ 40: "Brush",
9994
+ 48: "Orchestra",
9995
+ 56: "SFX",
9996
+ }
9997
+
9998
+ # Control changes list (without specifications):
9999
+ # https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
10000
+ # Undefined and general control changes are not considered here
10001
+ # All these attributes can take values from 0 to 127, with some of them being on/off
10002
+ CONTROL_CHANGES = {
10003
+ # MSB
10004
+ 0: "Bank Select",
10005
+ 1: "Modulation Depth",
10006
+ 2: "Breath Controller",
10007
+ 4: "Foot Controller",
10008
+ 5: "Portamento Time",
10009
+ 6: "Data Entry",
10010
+ 7: "Channel Volume",
10011
+ 8: "Balance",
10012
+ 10: "Pan",
10013
+ 11: "Expression Controller",
10014
+ # LSB
10015
+ 32: "Bank Select",
10016
+ 33: "Modulation Depth",
10017
+ 34: "Breath Controller",
10018
+ 36: "Foot Controller",
10019
+ 37: "Portamento Time",
10020
+ 38: "Data Entry",
10021
+ 39: "Channel Volume",
10022
+ 40: "Balance",
10023
+ 42: "Pan",
10024
+ 43: "Expression Controller",
10025
+ # On / Off control changes, ≤63 off, ≥64 on
10026
+ 64: "Damper Pedal",
10027
+ 65: "Portamento",
10028
+ 66: "Sostenuto",
10029
+ 67: "Soft Pedal",
10030
+ 68: "Legato Footswitch",
10031
+ 69: "Hold 2",
10032
+ # Continuous controls
10033
+ 70: "Sound Variation",
10034
+ 71: "Timbre/Harmonic Intensity",
10035
+ 72: "Release Time",
10036
+ 73: "Attack Time",
10037
+ 74: "Brightness",
10038
+ 75: "Decay Time",
10039
+ 76: "Vibrato Rate",
10040
+ 77: "Vibrato Depth",
10041
+ 78: "Vibrato Delay",
10042
+ 84: "Portamento Control",
10043
+ 88: "High Resolution Velocity Prefix",
10044
+ # Effects depths
10045
+ 91: "Reverb Depth",
10046
+ 92: "Tremolo Depth",
10047
+ 93: "Chorus Depth",
10048
+ 94: "Celeste Depth",
10049
+ 95: "Phaser Depth",
10050
+ # Registered parameters numbers
10051
+ 96: "Data Increment",
10052
+ 97: "Data Decrement",
10053
+ # 98: 'Non-Registered Parameter Number (NRPN) - LSB',
10054
+ # 99: 'Non-Registered Parameter Number (NRPN) - MSB',
10055
+ 100: "Registered Parameter Number (RPN) - LSB",
10056
+ 101: "Registered Parameter Number (RPN) - MSB",
10057
+ # Channel mode controls
10058
+ 120: "All Sound Off",
10059
+ 121: "Reset All Controllers",
10060
+ 122: "Local Control On/Off",
10061
+ 123: "All Notes Off",
10062
+ 124: "Omni Mode Off", # + all notes off
10063
+ 125: "Omni Mode On", # + all notes off
10064
+ 126: "Mono Mode On", # + poly off, + all notes off
10065
+ 127: "Poly Mode On", # + mono off, +all notes off
10066
+ }
10067
+
10068
+ ###################################################################################
10069
+
10070
+ def patches_onset_times(escore_notes, times_idx=1, patches_idx=6):
10071
+
10072
+ patches = [e[patches_idx] for e in escore_notes]
10073
+
10074
+ patches_oset = ordered_set(patches)
10075
+
10076
+ patches_onset_times = []
10077
+
10078
+ for p in patches_oset:
10079
+ for e in escore_notes:
10080
+ if e[patches_idx] == p:
10081
+ patches_onset_times.append([p, e[times_idx]])
10082
+ break
10083
+
10084
+ return patches_onset_times
10085
+
10086
+ ###################################################################################
10087
+
10088
+ def count_escore_notes_patches(escore_notes, patches_idx=6):
10089
+
10090
+ patches = [e[patches_idx] for e in escore_notes]
10091
+
10092
+ return Counter(patches).most_common()
10093
+
10094
+ ###################################################################################
10095
+
10096
+ def escore_notes_monoponic_melodies(escore_notes,
10097
+ bad_notes_ratio=0.0,
10098
+ times_idx=1,
10099
+ patches_idx=6
10100
+ ):
10101
+
10102
+ patches = escore_notes_patches(escore_notes, patches_index=patches_idx)
10103
+
10104
+ monophonic_melodies = []
10105
+
10106
+ for p in patches:
10107
+ patch_score = [e for e in escore_notes if e[patches_idx] == p]
10108
+
10109
+ ps_times = [e[times_idx] for e in patch_score]
10110
+
10111
+ if len(ps_times) <= len(set(ps_times)) * (1+bad_notes_ratio):
10112
+ monophonic_melodies.append([p, len(patch_score)])
10113
+
10114
+ return monophonic_melodies
10115
+
10116
+ ###################################################################################
10117
+
10118
+ from itertools import groupby
10119
+ from operator import itemgetter
10120
+
10121
+ def group_by_threshold(data, threshold, groupby_idx):
10122
+
10123
+ data.sort(key=itemgetter(groupby_idx))
10124
+
10125
+ grouped_data = []
10126
+ cluster = []
10127
+
10128
+ for i, item in enumerate(data):
10129
+ if not cluster:
10130
+ cluster.append(item)
10131
+ elif abs(item[groupby_idx] - cluster[-1][groupby_idx]) <= threshold:
10132
+ cluster.append(item)
10133
+ else:
10134
+ grouped_data.append(cluster)
10135
+ cluster = [item]
10136
+
10137
+ if cluster:
10138
+ grouped_data.append(cluster)
10139
+
10140
+ return grouped_data
10141
+
10142
+ ###################################################################################
10143
+
10144
+ def split_escore_notes_by_time(escore_notes, time_threshold=256):
10145
+
10146
+ dscore = delta_score_notes(escore_notes, timings_clip_value=time_threshold-1)
10147
+
10148
+ score_chunks = []
10149
+
10150
+ ctime = 0
10151
+ pchunk_idx = 0
10152
+
10153
+ for i, e in enumerate(dscore):
10154
+
10155
+ ctime += e[1]
10156
+
10157
+ if ctime >= time_threshold:
10158
+ score_chunks.append(escore_notes[pchunk_idx:i])
10159
+ pchunk_idx = i
10160
+ ctime = 0
10161
+
10162
+ return score_chunks
10163
+
10164
+ ###################################################################################
10165
+
10166
+ def escore_notes_grouped_patches(escore_notes, time_threshold=256):
10167
+
10168
+ split_score_chunks = split_escore_notes_by_time(escore_notes,
10169
+ time_threshold=time_threshold
10170
+ )
10171
+
10172
+ chunks_patches = []
10173
+
10174
+ for s in split_score_chunks:
10175
+ chunks_patches.append(escore_notes_patches(s))
10176
+
10177
+ return chunks_patches
10178
+
10179
+ ###################################################################################
10180
+
10181
+ def computeLPSArray(pattern, M, lps):
10182
+ length = 0
10183
+ i = 1
10184
+
10185
+ lps[0] = 0
10186
+
10187
+ while i < M:
10188
+ if pattern[i] == pattern[length]:
10189
+ length += 1
10190
+ lps[i] = length
10191
+ i += 1
10192
+ else:
10193
+ if length != 0:
10194
+ length = lps[length-1]
10195
+ else:
10196
+ lps[i] = 0
10197
+ i += 1
10198
+
10199
+ ###################################################################################
10200
+
10201
+ def find_pattern_idxs(sub_pattern, pattern):
10202
+
10203
+ lst = pattern
10204
+ pattern = sub_pattern
10205
+
10206
+ M = len(pattern)
10207
+ N = len(lst)
10208
+
10209
+ lps = [0] * M
10210
+ j = 0 # index for pattern[]
10211
+
10212
+ computeLPSArray(pattern, M, lps)
10213
+
10214
+ i = 0 # index for lst[]
10215
+ indexes = []
10216
+
10217
+ while i < N:
10218
+ if pattern[j] == lst[i]:
10219
+ i += 1
10220
+ j += 1
10221
+
10222
+ if j == M:
10223
+ end_index = i - 1
10224
+ start_index = end_index - M + 1
10225
+ indexes.append((start_index, end_index))
10226
+ j = lps[j-1]
10227
+ elif i < N and pattern[j] != lst[i]:
10228
+ if j != 0:
10229
+ j = lps[j-1]
10230
+ else:
10231
+ i += 1
10232
+
10233
+ return indexes
10234
+
10235
+ ###################################################################################
10236
+
10237
+ def escore_notes_patch_lrno_patterns(escore_notes,
10238
+ patch=0,
10239
+ zero_score_timings=False,
10240
+ pitches_idx=4,
10241
+ patches_idx=6
10242
+ ):
10243
+
10244
+ patch_escore = [e for e in escore_notes if e[patches_idx] == patch]
10245
+
10246
+ if patch_escore:
10247
+
10248
+ patch_cscore = chordify_score([1000, patch_escore])
10249
+
10250
+ patch_tscore = []
10251
+
10252
+ for c in patch_cscore:
10253
+
10254
+ tones_chord = sorted(set([p[pitches_idx] % 12 for p in c]))
10255
+
10256
+ if tones_chord not in ALL_CHORDS_SORTED:
10257
+ tnoes_chord = check_and_fix_tones_chord(tones_chord)
10258
+
10259
+ patch_tscore.append(ALL_CHORDS_SORTED.index(tones_chord))
10260
+
10261
+ pattern = find_lrno_pattern_fast(patch_tscore)
10262
+
10263
+ patterns_idxs = find_pattern_idxs(pattern, patch_tscore)
10264
+
10265
+ patch_lrno_scores = []
10266
+
10267
+ for idxs in patterns_idxs:
10268
+
10269
+ score = patch_escore[idxs[0]:idxs[1]]
10270
+
10271
+ if zero_score_timings:
10272
+ score = recalculate_score_timings(score)
10273
+
10274
+ patch_lrno_scores.append(score)
10275
+
10276
+ return patch_lrno_scores
10277
+
10278
+ else:
10279
+ return []
10280
+
10281
+ ###################################################################################
10282
+
10283
+ 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],
10284
+ [0, 2, 4, 6, 9], [0, 2, 4, 6, 10], [0, 2, 4, 7], [0, 2, 4, 7, 9],
10285
+ [0, 2, 4, 7, 10], [0, 2, 4, 8], [0, 2, 4, 8, 10], [0, 2, 4, 9], [0, 2, 4, 10],
10286
+ [0, 2, 5], [0, 2, 5, 7], [0, 2, 5, 7, 9], [0, 2, 5, 7, 10], [0, 2, 5, 8],
10287
+ [0, 2, 5, 8, 10], [0, 2, 5, 9], [0, 2, 5, 10], [0, 2, 6], [0, 2, 6, 8],
10288
+ [0, 2, 6, 8, 10], [0, 2, 6, 9], [0, 2, 6, 10], [0, 2, 7], [0, 2, 7, 9],
10289
+ [0, 2, 7, 10], [0, 2, 8], [0, 2, 8, 10], [0, 2, 9], [0, 2, 10], [0, 3],
10290
+ [0, 3, 5], [0, 3, 5, 7], [0, 3, 5, 7, 9], [0, 3, 5, 7, 10], [0, 3, 5, 8],
10291
+ [0, 3, 5, 8, 10], [0, 3, 5, 9], [0, 3, 5, 10], [0, 3, 6], [0, 3, 6, 8],
10292
+ [0, 3, 6, 8, 10], [0, 3, 6, 9], [0, 3, 6, 10], [0, 3, 7], [0, 3, 7, 9],
10293
+ [0, 3, 7, 10], [0, 3, 8], [0, 3, 8, 10], [0, 3, 9], [0, 3, 10], [0, 4],
10294
+ [0, 4, 6], [0, 4, 6, 8], [0, 4, 6, 8, 10], [0, 4, 6, 9], [0, 4, 6, 10],
10295
+ [0, 4, 7], [0, 4, 7, 9], [0, 4, 7, 10], [0, 4, 8], [0, 4, 8, 10], [0, 4, 9],
10296
+ [0, 4, 10], [0, 5], [0, 5, 7], [0, 5, 7, 9], [0, 5, 7, 10], [0, 5, 8],
10297
+ [0, 5, 8, 10], [0, 5, 9], [0, 5, 10], [0, 6], [0, 6, 8], [0, 6, 8, 10],
10298
+ [0, 6, 9], [0, 6, 10], [0, 7], [0, 7, 9], [0, 7, 10], [0, 8], [0, 8, 10],
10299
+ [0, 9], [0, 10]]
10300
+
10301
  ###################################################################################
10302
  #
10303
  # This is the end of the TMIDI X Python module