asigalov61 commited on
Commit
d3fa8c7
1 Parent(s): 6aeacba

Upload 2 files

Browse files
Files changed (2) hide show
  1. TMIDIX.py +1911 -17
  2. midi_to_colab_audio.py +0 -0
TMIDIX.py CHANGED
@@ -41,7 +41,9 @@ r'''############################################################################
41
  # Please see TMIDI 2.3/tegridy-tools repo for full MIDI.py module code
42
  #
43
  # Or you can always download the latest full version from:
44
- # https://pjb.com.au/
 
 
45
  #
46
  # Copyright 2020 Peter Billam
47
  #
@@ -172,7 +174,7 @@ Translates a "score" into MIDI, using score2opus() then opus2midi()
172
 
173
  #--------------------------- Decoding stuff ------------------------
174
 
175
- def midi2opus(midi=b''):
176
  r'''Translates MIDI into a "opus". For a description of the
177
  "opus" format, see opus2midi()
178
  '''
@@ -184,7 +186,8 @@ def midi2opus(midi=b''):
184
  if id != b'MThd':
185
  _warn("midi2opus: midi starts with "+str(id)+" instead of 'MThd'")
186
  _clean_up_warnings()
187
- return [1000,[],]
 
188
  [length, format, tracks_expected, ticks] = struct.unpack(
189
  '>IHHH', bytes(my_midi[4:14]))
190
  if length != 6:
@@ -264,23 +267,101 @@ see opus2midi() and score2opus().
264
  _clean_up_warnings()
265
  return score
266
 
267
- def midi2score(midi=b''):
268
  r'''
269
  Translates MIDI into a "score", using midi2opus() then opus2score()
270
  '''
271
- return opus2score(midi2opus(midi))
272
 
273
- def midi2ms_score(midi=b''):
274
  r'''
275
  Translates MIDI into a "score" with one beat per second and one
276
  tick per millisecond, using midi2opus() then to_millisecs()
277
  then opus2score()
278
  '''
279
- return opus2score(to_millisecs(midi2opus(midi)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
  #------------------------ Other Transformations ---------------------
282
 
283
- def to_millisecs(old_opus=None, desired_time_in_ms=1):
284
  r'''Recallibrates all the times in an "opus" to use one beat
285
  per second and one tick per millisecond. This makes it
286
  hard to retrieve any information about beats or barlines,
@@ -320,7 +401,11 @@ but it does make it easy to mix different scores together.
320
  ticks_so_far = 0
321
  ms_so_far = 0.0
322
  previous_ms_so_far = 0.0
323
- new_track = [['set_tempo',0,1000000 * desired_time_in_ms],] # new "crochet" is 1 sec
 
 
 
 
324
  for old_event in old_opus[itrack]:
325
  # detect if ticks2tempo has something before this event
326
  # 20160702 if ticks2tempo is at the same time, leave it
@@ -336,9 +421,19 @@ but it does make it easy to mix different scores together.
336
  new_event = copy.deepcopy(old_event) # now handle the new event
337
  ms_so_far += (ms_per_old_tick * old_event[1] * desired_time_in_ms)
338
  new_event[1] = round(ms_so_far - previous_ms_so_far)
339
- if old_event[0] != 'set_tempo':
340
- previous_ms_so_far = ms_so_far
341
- new_track.append(new_event)
 
 
 
 
 
 
 
 
 
 
342
  ticks_so_far += event_delta_ticks
343
  new_opus.append(new_track)
344
  itrack += 1
@@ -1378,6 +1473,7 @@ import tqdm
1378
 
1379
  from itertools import zip_longest
1380
  from itertools import groupby
 
1381
 
1382
  from operator import itemgetter
1383
 
@@ -1389,6 +1485,8 @@ from difflib import SequenceMatcher as SM
1389
 
1390
  import statistics
1391
 
 
 
1392
  ###################################################################################
1393
  #
1394
  # Original TMIDI Tegridy helper functions
@@ -1499,7 +1597,8 @@ def Tegridy_SONG_to_MIDI_Converter(SONG,
1499
  number_of_ticks_per_quarter = 425,
1500
  list_of_MIDI_patches = [0, 24, 32, 40, 42, 46, 56, 71, 73, 0, 0, 0, 0, 0, 0, 0],
1501
  output_file_name = 'TMIDI-Composition',
1502
- text_encoding='ISO-8859-1'):
 
1503
 
1504
  '''Tegridy SONG to MIDI Converter
1505
 
@@ -1516,8 +1615,9 @@ def Tegridy_SONG_to_MIDI_Converter(SONG,
1516
 
1517
  Project Los Angeles
1518
  Tegridy Code 2020'''
1519
-
1520
- print('Converting to MIDI. Please stand-by...')
 
1521
 
1522
  output_header = [number_of_ticks_per_quarter,
1523
  [['track_name', 0, bytes(output_signature, text_encoding)]]]
@@ -1549,7 +1649,222 @@ def Tegridy_SONG_to_MIDI_Converter(SONG,
1549
  midi_file.write(midi_data)
1550
  midi_file.close()
1551
 
1552
- print('Done! Enjoy! :)')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1553
 
1554
  return detailed_MIDI_stats
1555
 
@@ -3197,6 +3512,1585 @@ def Tegridy_Split_List(list_to_split, split_value=0):
3197
 
3198
  ###################################################################################
3199
 
3200
- # This is the end of the TMIDI X Python module
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3201
 
3202
  ###################################################################################
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  # Please see TMIDI 2.3/tegridy-tools repo for full MIDI.py module code
42
  #
43
  # Or you can always download the latest full version from:
44
+ #
45
+ # https://pjb.com.au/
46
+ # https://peterbillam.gitlab.io/miditools/
47
  #
48
  # Copyright 2020 Peter Billam
49
  #
 
174
 
175
  #--------------------------- Decoding stuff ------------------------
176
 
177
+ def midi2opus(midi=b'', do_not_check_MIDI_signature=False):
178
  r'''Translates MIDI into a "opus". For a description of the
179
  "opus" format, see opus2midi()
180
  '''
 
186
  if id != b'MThd':
187
  _warn("midi2opus: midi starts with "+str(id)+" instead of 'MThd'")
188
  _clean_up_warnings()
189
+ if do_not_check_MIDI_signature == False:
190
+ return [1000,[],]
191
  [length, format, tracks_expected, ticks] = struct.unpack(
192
  '>IHHH', bytes(my_midi[4:14]))
193
  if length != 6:
 
267
  _clean_up_warnings()
268
  return score
269
 
270
+ def midi2score(midi=b'', do_not_check_MIDI_signature=False):
271
  r'''
272
  Translates MIDI into a "score", using midi2opus() then opus2score()
273
  '''
274
+ return opus2score(midi2opus(midi, do_not_check_MIDI_signature))
275
 
276
+ def midi2ms_score(midi=b'', do_not_check_MIDI_signature=False):
277
  r'''
278
  Translates MIDI into a "score" with one beat per second and one
279
  tick per millisecond, using midi2opus() then to_millisecs()
280
  then opus2score()
281
  '''
282
+ return opus2score(to_millisecs(midi2opus(midi, do_not_check_MIDI_signature)))
283
+
284
+ def midi2single_track_ms_score(midi_path_or_bytes,
285
+ recalculate_channels = False,
286
+ pass_old_timings_events= False,
287
+ verbose = False,
288
+ do_not_check_MIDI_signature=False
289
+ ):
290
+ r'''
291
+ Translates MIDI into a single track "score" with 16 instruments and one beat per second and one
292
+ tick per millisecond
293
+ '''
294
+
295
+ if type(midi_path_or_bytes) == bytes:
296
+ midi_data = midi_path_or_bytes
297
+
298
+ elif type(midi_path_or_bytes) == str:
299
+ midi_data = open(midi_path_or_bytes, 'rb').read()
300
+
301
+ score = midi2score(midi_data, do_not_check_MIDI_signature)
302
+
303
+ if recalculate_channels:
304
+
305
+ events_matrixes = []
306
+
307
+ itrack = 1
308
+ events_matrixes_channels = []
309
+ while itrack < len(score):
310
+ events_matrix = []
311
+ for event in score[itrack]:
312
+ if event[0] == 'note' and event[3] != 9:
313
+ event[3] = (16 * (itrack-1)) + event[3]
314
+ if event[3] not in events_matrixes_channels:
315
+ events_matrixes_channels.append(event[3])
316
+
317
+ events_matrix.append(event)
318
+ events_matrixes.append(events_matrix)
319
+ itrack += 1
320
+
321
+ events_matrix1 = []
322
+ for e in events_matrixes:
323
+ events_matrix1.extend(e)
324
+
325
+ if verbose:
326
+ if len(events_matrixes_channels) > 16:
327
+ print('MIDI has', len(events_matrixes_channels), 'instruments!', len(events_matrixes_channels) - 16, 'instrument(s) will be removed!')
328
+
329
+ for e in events_matrix1:
330
+ if e[0] == 'note' and e[3] != 9:
331
+ if e[3] in events_matrixes_channels[:15]:
332
+ if events_matrixes_channels[:15].index(e[3]) < 9:
333
+ e[3] = events_matrixes_channels[:15].index(e[3])
334
+ else:
335
+ e[3] = events_matrixes_channels[:15].index(e[3])+1
336
+ else:
337
+ events_matrix1.remove(e)
338
+
339
+ if e[0] in ['patch_change', 'control_change', 'channel_after_touch', 'key_after_touch', 'pitch_wheel_change'] and e[2] != 9:
340
+ if e[2] in [e % 16 for e in events_matrixes_channels[:15]]:
341
+ if [e % 16 for e in events_matrixes_channels[:15]].index(e[2]) < 9:
342
+ e[2] = [e % 16 for e in events_matrixes_channels[:15]].index(e[2])
343
+ else:
344
+ e[2] = [e % 16 for e in events_matrixes_channels[:15]].index(e[2])+1
345
+ else:
346
+ events_matrix1.remove(e)
347
+
348
+ else:
349
+ events_matrix1 = []
350
+ itrack = 1
351
+
352
+ while itrack < len(score):
353
+ for event in score[itrack]:
354
+ events_matrix1.append(event)
355
+ itrack += 1
356
+
357
+ opus = score2opus([score[0], events_matrix1])
358
+ ms_score = opus2score(to_millisecs(opus, pass_old_timings_events=pass_old_timings_events))
359
+
360
+ return ms_score
361
 
362
  #------------------------ Other Transformations ---------------------
363
 
364
+ def to_millisecs(old_opus=None, desired_time_in_ms=1, pass_old_timings_events = False):
365
  r'''Recallibrates all the times in an "opus" to use one beat
366
  per second and one tick per millisecond. This makes it
367
  hard to retrieve any information about beats or barlines,
 
401
  ticks_so_far = 0
402
  ms_so_far = 0.0
403
  previous_ms_so_far = 0.0
404
+
405
+ if pass_old_timings_events:
406
+ new_track = [['set_tempo',0,1000000 * desired_time_in_ms],['old_tpq', 0, old_tpq]] # new "crochet" is 1 sec
407
+ else:
408
+ new_track = [['set_tempo',0,1000000 * desired_time_in_ms],] # new "crochet" is 1 sec
409
  for old_event in old_opus[itrack]:
410
  # detect if ticks2tempo has something before this event
411
  # 20160702 if ticks2tempo is at the same time, leave it
 
421
  new_event = copy.deepcopy(old_event) # now handle the new event
422
  ms_so_far += (ms_per_old_tick * old_event[1] * desired_time_in_ms)
423
  new_event[1] = round(ms_so_far - previous_ms_so_far)
424
+
425
+ if pass_old_timings_events:
426
+ if old_event[0] != 'set_tempo':
427
+ previous_ms_so_far = ms_so_far
428
+ new_track.append(new_event)
429
+ else:
430
+ new_event[0] = 'old_set_tempo'
431
+ previous_ms_so_far = ms_so_far
432
+ new_track.append(new_event)
433
+ else:
434
+ if old_event[0] != 'set_tempo':
435
+ previous_ms_so_far = ms_so_far
436
+ new_track.append(new_event)
437
  ticks_so_far += event_delta_ticks
438
  new_opus.append(new_track)
439
  itrack += 1
 
1473
 
1474
  from itertools import zip_longest
1475
  from itertools import groupby
1476
+ from collections import Counter
1477
 
1478
  from operator import itemgetter
1479
 
 
1485
 
1486
  import statistics
1487
 
1488
+ import matplotlib.pyplot as plt
1489
+
1490
  ###################################################################################
1491
  #
1492
  # Original TMIDI Tegridy helper functions
 
1597
  number_of_ticks_per_quarter = 425,
1598
  list_of_MIDI_patches = [0, 24, 32, 40, 42, 46, 56, 71, 73, 0, 0, 0, 0, 0, 0, 0],
1599
  output_file_name = 'TMIDI-Composition',
1600
+ text_encoding='ISO-8859-1',
1601
+ verbose=True):
1602
 
1603
  '''Tegridy SONG to MIDI Converter
1604
 
 
1615
 
1616
  Project Los Angeles
1617
  Tegridy Code 2020'''
1618
+
1619
+ if verbose:
1620
+ print('Converting to MIDI. Please stand-by...')
1621
 
1622
  output_header = [number_of_ticks_per_quarter,
1623
  [['track_name', 0, bytes(output_signature, text_encoding)]]]
 
1649
  midi_file.write(midi_data)
1650
  midi_file.close()
1651
 
1652
+ if verbose:
1653
+ print('Done! Enjoy! :)')
1654
+
1655
+ return detailed_MIDI_stats
1656
+
1657
+ ###################################################################################
1658
+
1659
+ def Tegridy_ms_SONG_to_MIDI_Converter(ms_SONG,
1660
+ output_signature = 'Tegridy TMIDI Module',
1661
+ track_name = 'Composition Track',
1662
+ list_of_MIDI_patches = [0, 24, 32, 40, 42, 46, 56, 71, 73, 0, 0, 0, 0, 0, 0, 0],
1663
+ output_file_name = 'TMIDI-Composition',
1664
+ text_encoding='ISO-8859-1',
1665
+ timings_multiplier=1,
1666
+ verbose=True
1667
+ ):
1668
+
1669
+ '''Tegridy milisecond SONG to MIDI Converter
1670
+
1671
+ Input: Input ms SONG in TMIDI ms SONG/MIDI.py ms Score format
1672
+ Output MIDI Track 0 name / MIDI Signature
1673
+ Output MIDI Track 1 name / Composition track name
1674
+ List of 16 MIDI patch numbers for output MIDI. Def. is MuseNet compatible patches.
1675
+ Output file name w/o .mid extension.
1676
+ Optional text encoding if you are working with text_events/lyrics. This is especially useful for Karaoke. Please note that anything but ISO-8859-1 is a non-standard way of encoding text_events according to MIDI specs.
1677
+ Optional timings multiplier
1678
+ Optional verbose output
1679
+
1680
+ Output: MIDI File
1681
+ Detailed MIDI stats
1682
+
1683
+ Project Los Angeles
1684
+ Tegridy Code 2024'''
1685
+
1686
+ if verbose:
1687
+ print('Converting to MIDI. Please stand-by...')
1688
+
1689
+ output_header = [1000,
1690
+ [['set_tempo', 0, 1000000],
1691
+ ['time_signature', 0, 4, 2, 24, 8],
1692
+ ['track_name', 0, bytes(output_signature, text_encoding)]]]
1693
+
1694
+ patch_list = [['patch_change', 0, 0, list_of_MIDI_patches[0]],
1695
+ ['patch_change', 0, 1, list_of_MIDI_patches[1]],
1696
+ ['patch_change', 0, 2, list_of_MIDI_patches[2]],
1697
+ ['patch_change', 0, 3, list_of_MIDI_patches[3]],
1698
+ ['patch_change', 0, 4, list_of_MIDI_patches[4]],
1699
+ ['patch_change', 0, 5, list_of_MIDI_patches[5]],
1700
+ ['patch_change', 0, 6, list_of_MIDI_patches[6]],
1701
+ ['patch_change', 0, 7, list_of_MIDI_patches[7]],
1702
+ ['patch_change', 0, 8, list_of_MIDI_patches[8]],
1703
+ ['patch_change', 0, 9, list_of_MIDI_patches[9]],
1704
+ ['patch_change', 0, 10, list_of_MIDI_patches[10]],
1705
+ ['patch_change', 0, 11, list_of_MIDI_patches[11]],
1706
+ ['patch_change', 0, 12, list_of_MIDI_patches[12]],
1707
+ ['patch_change', 0, 13, list_of_MIDI_patches[13]],
1708
+ ['patch_change', 0, 14, list_of_MIDI_patches[14]],
1709
+ ['patch_change', 0, 15, list_of_MIDI_patches[15]],
1710
+ ['track_name', 0, bytes(track_name, text_encoding)]]
1711
+
1712
+ SONG = copy.deepcopy(ms_SONG)
1713
+
1714
+ if timings_multiplier != 1:
1715
+ for S in SONG:
1716
+ S[1] = S[1] * timings_multiplier
1717
+ if S[0] == 'note':
1718
+ S[2] = S[2] * timings_multiplier
1719
+
1720
+ output = output_header + [patch_list + SONG]
1721
+
1722
+ midi_data = score2midi(output, text_encoding)
1723
+ detailed_MIDI_stats = score2stats(output)
1724
+
1725
+ with open(output_file_name + '.mid', 'wb') as midi_file:
1726
+ midi_file.write(midi_data)
1727
+ midi_file.close()
1728
+
1729
+ if verbose:
1730
+ print('Done! Enjoy! :)')
1731
+
1732
+ return detailed_MIDI_stats
1733
+
1734
+ ###################################################################################
1735
+
1736
+ def hsv_to_rgb(h, s, v):
1737
+ if s == 0.0:
1738
+ return v, v, v
1739
+ i = int(h*6.0)
1740
+ f = (h*6.0) - i
1741
+ p = v*(1.0 - s)
1742
+ q = v*(1.0 - s*f)
1743
+ t = v*(1.0 - s*(1.0-f))
1744
+ i = i%6
1745
+ return [(v, t, p), (q, v, p), (p, v, t), (p, q, v), (t, p, v), (v, p, q)][i]
1746
+
1747
+ def generate_colors(n):
1748
+ return [hsv_to_rgb(i/n, 1, 1) for i in range(n)]
1749
+
1750
+ def add_arrays(a, b):
1751
+ return [sum(pair) for pair in zip(a, b)]
1752
+
1753
+ #-------------------------------------------------------------------------------
1754
+
1755
+ def plot_ms_SONG(ms_song,
1756
+ preview_length_in_notes=0,
1757
+ block_lines_times_list = None,
1758
+ plot_title='ms Song',
1759
+ max_num_colors=129,
1760
+ drums_color_num=128,
1761
+ plot_size=(11,4),
1762
+ note_height = 0.75,
1763
+ show_grid_lines=False,
1764
+ return_plt = False
1765
+ ):
1766
+
1767
+ '''Tegridy ms SONG plotter/vizualizer'''
1768
+
1769
+ notes = [s for s in ms_song if s[0] == 'note']
1770
+
1771
+ if (len(max(notes, key=len)) != 7) and (len(min(notes, key=len)) != 7):
1772
+ print('The song notes do not have patches information')
1773
+ print('Ploease add patches to the notes in the song')
1774
+
1775
+ else:
1776
+
1777
+ start_times = [s[1] / 1000 for s in notes]
1778
+ durations = [s[2] / 1000 for s in notes]
1779
+ pitches = [s[4] for s in notes]
1780
+ patches = [s[6] for s in notes]
1781
+
1782
+ colors = generate_colors(max_num_colors)
1783
+ colors[drums_color_num] = (1, 1, 1)
1784
+
1785
+ pbl = notes[preview_length_in_notes][1] / 1000
1786
+
1787
+ fig, ax = plt.subplots(figsize=plot_size)
1788
+ #fig, ax = plt.subplots()
1789
+
1790
+ # Create a rectangle for each note with color based on patch number
1791
+ for start, duration, pitch, patch in zip(start_times, durations, pitches, patches):
1792
+ rect = plt.Rectangle((start, pitch), duration, note_height, facecolor=colors[patch])
1793
+ ax.add_patch(rect)
1794
+
1795
+ # Set the limits of the plot
1796
+ ax.set_xlim([min(start_times), max(add_arrays(start_times, durations))])
1797
+ ax.set_ylim([min(pitches)-1, max(pitches)+1])
1798
+
1799
+ # Set the background color to black
1800
+ ax.set_facecolor('black')
1801
+ fig.patch.set_facecolor('white')
1802
+
1803
+ if preview_length_in_notes > 0:
1804
+ ax.axvline(x=pbl, c='white')
1805
+
1806
+ if block_lines_times_list:
1807
+ for bl in block_lines_times_list:
1808
+ ax.axvline(x=bl, c='white')
1809
+
1810
+ if show_grid_lines:
1811
+ ax.grid(color='white')
1812
+
1813
+ plt.xlabel('Time (s)', c='black')
1814
+ plt.ylabel('MIDI Pitch', c='black')
1815
+
1816
+ plt.title(plot_title)
1817
+
1818
+ if return_plt:
1819
+ return fig
1820
+
1821
+ plt.show()
1822
+
1823
+ ###################################################################################
1824
+
1825
+ def Tegridy_SONG_to_Full_MIDI_Converter(SONG,
1826
+ output_signature = 'Tegridy TMIDI Module',
1827
+ track_name = 'Composition Track',
1828
+ number_of_ticks_per_quarter = 1000,
1829
+ output_file_name = 'TMIDI-Composition',
1830
+ text_encoding='ISO-8859-1',
1831
+ verbose=True):
1832
+
1833
+ '''Tegridy SONG to Full MIDI Converter
1834
+
1835
+ Input: Input SONG in Full TMIDI SONG/MIDI.py Score format
1836
+ Output MIDI Track 0 name / MIDI Signature
1837
+ Output MIDI Track 1 name / Composition track name
1838
+ Number of ticks per quarter for the output MIDI
1839
+ Output file name w/o .mid extension.
1840
+ Optional text encoding if you are working with text_events/lyrics. This is especially useful for Karaoke. Please note that anything but ISO-8859-1 is a non-standard way of encoding text_events according to MIDI specs.
1841
+
1842
+ Output: MIDI File
1843
+ Detailed MIDI stats
1844
+
1845
+ Project Los Angeles
1846
+ Tegridy Code 2023'''
1847
+
1848
+ if verbose:
1849
+ print('Converting to MIDI. Please stand-by...')
1850
+
1851
+ output_header = [number_of_ticks_per_quarter,
1852
+ [['set_tempo', 0, 1000000],
1853
+ ['track_name', 0, bytes(output_signature, text_encoding)]]]
1854
+
1855
+ song_track = [['track_name', 0, bytes(track_name, text_encoding)]]
1856
+
1857
+ output = output_header + [song_track + SONG]
1858
+
1859
+ midi_data = score2midi(output, text_encoding)
1860
+ detailed_MIDI_stats = score2stats(output)
1861
+
1862
+ with open(output_file_name + '.mid', 'wb') as midi_file:
1863
+ midi_file.write(midi_data)
1864
+ midi_file.close()
1865
+
1866
+ if verbose:
1867
+ print('Done! Enjoy! :)')
1868
 
1869
  return detailed_MIDI_stats
1870
 
 
3512
 
3513
  ###################################################################################
3514
 
3515
+ # Binary chords functions
3516
+
3517
+ def tones_chord_to_bits(chord):
3518
+ bits = [0] * 12
3519
+ for num in chord:
3520
+ bits[num] = 1
3521
+
3522
+ return bits
3523
+
3524
+ def bits_to_tones_chord(bits):
3525
+ return [i for i, bit in enumerate(bits) if bit == 1]
3526
+
3527
+ def shift_bits(bits, n):
3528
+ return bits[-n:] + bits[:-n]
3529
+
3530
+ def bits_to_int(bits, shift_bits_value=0):
3531
+ bits = shift_bits(bits, shift_bits_value)
3532
+ result = 0
3533
+ for bit in bits:
3534
+ result = (result << 1) | bit
3535
+
3536
+ return result
3537
+
3538
+ def int_to_bits(n):
3539
+ bits = [0] * 12
3540
+ for i in range(12):
3541
+ bits[11 - i] = n % 2
3542
+ n //= 2
3543
+
3544
+ return bits
3545
+
3546
+ def bad_chord(chord):
3547
+ bad = any(b - a == 1 for a, b in zip(chord, chord[1:]))
3548
+ if (0 in chord) and (11 in chord):
3549
+ bad = True
3550
+
3551
+ return bad
3552
+
3553
+ def pitches_chord_to_int(pitches_chord, tones_transpose_value=0):
3554
+
3555
+ pitches_chord = [x for x in pitches_chord if 0 < x < 128]
3556
+
3557
+ if not (-12 < tones_transpose_value < 12):
3558
+ tones_transpose_value = 0
3559
+
3560
+ tones_chord = sorted(list(set([c % 12 for c in sorted(list(set(pitches_chord)))])))
3561
+ bits = tones_chord_to_bits(tones_chord)
3562
+ integer = bits_to_int(bits, shift_bits_value=tones_transpose_value)
3563
+
3564
+ return integer
3565
+
3566
+ def int_to_pitches_chord(integer, chord_base_pitch=60):
3567
+ if 0 < integer < 4096:
3568
+ bits = int_to_bits(integer)
3569
+ tones_chord = bits_to_tones_chord(bits)
3570
+ if not bad_chord(tones_chord):
3571
+ pitches_chord = [t+chord_base_pitch for t in tones_chord]
3572
+ return [pitches_chord, tones_chord]
3573
+
3574
+ else:
3575
+ return 0 # Bad chord code
3576
+
3577
+ else:
3578
+ return -1 # Bad integer code
3579
+
3580
+ ###################################################################################
3581
+
3582
+ def bad_chord(chord):
3583
+ bad = any(b - a == 1 for a, b in zip(chord, chord[1:]))
3584
+ if (0 in chord) and (11 in chord):
3585
+ bad = True
3586
+
3587
+ return bad
3588
+
3589
+ def validate_pitches_chord(pitches_chord, return_sorted = True):
3590
+
3591
+ pitches_chord = sorted(list(set([x for x in pitches_chord if 0 < x < 128])))
3592
+
3593
+ tones_chord = sorted(list(set([c % 12 for c in sorted(list(set(pitches_chord)))])))
3594
+
3595
+ if not bad_chord(tones_chord):
3596
+ if return_sorted:
3597
+ pitches_chord.sort(reverse=True)
3598
+ return pitches_chord
3599
+
3600
+ else:
3601
+ if 0 in tones_chord and 11 in tones_chord:
3602
+ tones_chord.remove(0)
3603
+
3604
+ fixed_tones = [[a, b] for a, b in zip(tones_chord, tones_chord[1:]) if b-a != 1]
3605
+
3606
+ fixed_tones_chord = []
3607
+ for f in fixed_tones:
3608
+ fixed_tones_chord.extend(f)
3609
+ fixed_tones_chord = list(set(fixed_tones_chord))
3610
+
3611
+ fixed_pitches_chord = []
3612
+
3613
+ for p in pitches_chord:
3614
+ if (p % 12) in fixed_tones_chord:
3615
+ fixed_pitches_chord.append(p)
3616
+
3617
+ if return_sorted:
3618
+ fixed_pitches_chord.sort(reverse=True)
3619
+
3620
+ return fixed_pitches_chord
3621
+
3622
+ def validate_pitches(chord, channel_to_check = 0, return_sorted = True):
3623
+
3624
+ pitches_chord = sorted(list(set([x[4] for x in chord if 0 < x[4] < 128 and x[3] == channel_to_check])))
3625
+
3626
+ if pitches_chord:
3627
+
3628
+ tones_chord = sorted(list(set([c % 12 for c in sorted(list(set(pitches_chord)))])))
3629
+
3630
+ if not bad_chord(tones_chord):
3631
+ if return_sorted:
3632
+ chord.sort(key = lambda x: x[4], reverse=True)
3633
+ return chord
3634
+
3635
+ else:
3636
+ if 0 in tones_chord and 11 in tones_chord:
3637
+ tones_chord.remove(0)
3638
+
3639
+ fixed_tones = [[a, b] for a, b in zip(tones_chord, tones_chord[1:]) if b-a != 1]
3640
+
3641
+ fixed_tones_chord = []
3642
+ for f in fixed_tones:
3643
+ fixed_tones_chord.extend(f)
3644
+ fixed_tones_chord = list(set(fixed_tones_chord))
3645
+
3646
+ fixed_chord = []
3647
+
3648
+ for c in chord:
3649
+ if c[3] == channel_to_check:
3650
+ if (c[4] % 12) in fixed_tones_chord:
3651
+ fixed_chord.append(c)
3652
+ else:
3653
+ fixed_chord.append(c)
3654
+
3655
+ if return_sorted:
3656
+ fixed_chord.sort(key = lambda x: x[4], reverse=True)
3657
+
3658
+ return fixed_chord
3659
+
3660
+ else:
3661
+ chord.sort(key = lambda x: x[4], reverse=True)
3662
+ return chord
3663
+
3664
+ def adjust_score_velocities(score, max_velocity):
3665
+
3666
+ min_velocity = min([c[5] for c in score])
3667
+ max_velocity_all_channels = max([c[5] for c in score])
3668
+ min_velocity_ratio = min_velocity / max_velocity_all_channels
3669
+
3670
+ max_channel_velocity = max([c[5] for c in score])
3671
+ if max_channel_velocity < min_velocity:
3672
+ factor = max_velocity / min_velocity
3673
+ else:
3674
+ factor = max_velocity / max_channel_velocity
3675
+ for i in range(len(score)):
3676
+ score[i][5] = int(score[i][5] * factor)
3677
+
3678
+ def chordify_score(score,
3679
+ return_choridfied_score=True,
3680
+ return_detected_score_information=False
3681
+ ):
3682
+
3683
+ if score:
3684
+
3685
+ num_tracks = 1
3686
+ single_track_score = []
3687
+ score_num_ticks = 0
3688
+
3689
+ if type(score[0]) == int and len(score) > 1:
3690
+
3691
+ score_type = 'MIDI_PY'
3692
+ score_num_ticks = score[0]
3693
+
3694
+ while num_tracks < len(score):
3695
+ for event in score[num_tracks]:
3696
+ single_track_score.append(event)
3697
+ num_tracks += 1
3698
+
3699
+ else:
3700
+ score_type = 'CUSTOM'
3701
+ single_track_score = score
3702
+
3703
+ if single_track_score and single_track_score[0]:
3704
+
3705
+ try:
3706
+
3707
+ if type(single_track_score[0][0]) == str or single_track_score[0][0] == 'note':
3708
+ single_track_score.sort(key = lambda x: x[1])
3709
+ score_timings = [s[1] for s in single_track_score]
3710
+ else:
3711
+ score_timings = [s[0] for s in single_track_score]
3712
+
3713
+ is_score_time_absolute = lambda sct: all(x <= y for x, y in zip(sct, sct[1:]))
3714
+
3715
+ score_timings_type = ''
3716
+
3717
+ if is_score_time_absolute(score_timings):
3718
+ score_timings_type = 'ABS'
3719
+
3720
+ chords = []
3721
+ cho = []
3722
+
3723
+ if score_type == 'MIDI_PY':
3724
+ pe = single_track_score[0]
3725
+ else:
3726
+ pe = single_track_score[0]
3727
+
3728
+ for e in single_track_score:
3729
+
3730
+ if score_type == 'MIDI_PY':
3731
+ time = e[1]
3732
+ ptime = pe[1]
3733
+ else:
3734
+ time = e[0]
3735
+ ptime = pe[0]
3736
+
3737
+ if time == ptime:
3738
+ cho.append(e)
3739
+
3740
+ else:
3741
+ if len(cho) > 0:
3742
+ chords.append(cho)
3743
+ cho = []
3744
+ cho.append(e)
3745
+
3746
+ pe = e
3747
+
3748
+ if len(cho) > 0:
3749
+ chords.append(cho)
3750
+
3751
+ else:
3752
+ score_timings_type = 'REL'
3753
+
3754
+ chords = []
3755
+ cho = []
3756
+
3757
+ for e in single_track_score:
3758
+
3759
+ if score_type == 'MIDI_PY':
3760
+ time = e[1]
3761
+ else:
3762
+ time = e[0]
3763
+
3764
+ if time == 0:
3765
+ cho.append(e)
3766
+
3767
+ else:
3768
+ if len(cho) > 0:
3769
+ chords.append(cho)
3770
+ cho = []
3771
+ cho.append(e)
3772
+
3773
+ if len(cho) > 0:
3774
+ chords.append(cho)
3775
+
3776
+ requested_data = []
3777
+
3778
+ if return_detected_score_information:
3779
+
3780
+ detected_score_information = []
3781
+
3782
+ detected_score_information.append(['Score type', score_type])
3783
+ detected_score_information.append(['Score timings type', score_timings_type])
3784
+ detected_score_information.append(['Score tpq', score_num_ticks])
3785
+ detected_score_information.append(['Score number of tracks', num_tracks])
3786
+
3787
+ requested_data.append(detected_score_information)
3788
+
3789
+ if return_choridfied_score and return_detected_score_information:
3790
+ requested_data.append(chords)
3791
+
3792
+ if return_choridfied_score and not return_detected_score_information:
3793
+ requested_data.extend(chords)
3794
+
3795
+ return requested_data
3796
+
3797
+ except Exception as e:
3798
+ print('Error!')
3799
+ print('Check score for consistency and compatibility!')
3800
+ print('Exception detected:', e)
3801
+
3802
+ else:
3803
+ return None
3804
+
3805
+ else:
3806
+ return None
3807
+
3808
+ def fix_monophonic_score_durations(monophonic_score):
3809
+
3810
+ fixed_score = []
3811
+
3812
+ if monophonic_score[0][0] == 'note':
3813
+
3814
+ for i in range(len(monophonic_score)-1):
3815
+ note = monophonic_score[i]
3816
+
3817
+ nmt = monophonic_score[i+1][1]
3818
+
3819
+ if note[1]+note[2] >= nmt:
3820
+ note_dur = nmt-note[1]-1
3821
+ else:
3822
+ note_dur = note[2]
3823
+
3824
+ new_note = [note[0], note[1], note_dur] + note[3:]
3825
+
3826
+ fixed_score.append(new_note)
3827
+
3828
+ fixed_score.append(monophonic_score[-1])
3829
+
3830
+ elif type(monophonic_score[0][0]) == int:
3831
+
3832
+ for i in range(len(monophonic_score)-1):
3833
+ note = monophonic_score[i]
3834
+
3835
+ nmt = monophonic_score[i+1][0]
3836
+
3837
+ if note[0]+note[1] >= nmt:
3838
+ note_dur = nmt-note[0]-1
3839
+ else:
3840
+ note_dur = note[1]
3841
+
3842
+ new_note = [note[0], note_dur] + note[2:]
3843
+
3844
+ fixed_score.append(new_note)
3845
+
3846
+ fixed_score.append(monophonic_score[-1])
3847
+
3848
+ return fixed_score
3849
 
3850
  ###################################################################################
3851
+
3852
+ from itertools import product
3853
+
3854
+ ALL_CHORDS = [[0], [7], [5], [9], [2], [4], [11], [10], [8], [6], [3], [1], [0, 9], [2, 5],
3855
+ [4, 7], [7, 10], [2, 11], [0, 3], [6, 9], [1, 4], [8, 11], [5, 8], [1, 10],
3856
+ [3, 6], [0, 4], [5, 9], [7, 11], [0, 7], [0, 5], [2, 10], [2, 7], [2, 9],
3857
+ [2, 6], [4, 11], [4, 9], [3, 7], [5, 10], [1, 9], [0, 8], [6, 11], [3, 11],
3858
+ [4, 8], [3, 10], [3, 8], [1, 5], [1, 8], [1, 6], [6, 10], [3, 9], [4, 10],
3859
+ [1, 7], [0, 6], [2, 8], [5, 11], [5, 7], [0, 10], [0, 2], [9, 11], [7, 9],
3860
+ [2, 4], [4, 6], [3, 5], [8, 10], [6, 8], [1, 3], [1, 11], [2, 7, 11],
3861
+ [0, 4, 7], [0, 5, 9], [2, 6, 9], [2, 5, 10], [1, 4, 9], [4, 8, 11], [3, 7, 10],
3862
+ [0, 3, 8], [3, 6, 11], [1, 5, 8], [1, 6, 10], [0, 4, 9], [2, 5, 9], [4, 7, 11],
3863
+ [2, 7, 10], [2, 6, 11], [0, 3, 7], [0, 5, 8], [1, 4, 8], [1, 6, 9], [3, 8, 11],
3864
+ [1, 5, 10], [3, 6, 10], [2, 5, 11], [4, 7, 10], [3, 6, 9], [0, 6, 9],
3865
+ [0, 3, 9], [2, 8, 11], [2, 5, 8], [1, 7, 10], [1, 4, 7], [0, 3, 6], [1, 4, 10],
3866
+ [5, 8, 11], [2, 5, 7], [0, 7, 10], [0, 2, 9], [0, 3, 5], [6, 9, 11], [4, 7, 9],
3867
+ [2, 4, 11], [5, 8, 10], [1, 3, 10], [1, 4, 6], [3, 6, 8], [1, 8, 11],
3868
+ [5, 7, 11], [0, 4, 10], [3, 5, 9], [0, 2, 6], [1, 7, 9], [0, 7, 9], [5, 7, 10],
3869
+ [2, 8, 10], [3, 9, 11], [0, 2, 5], [2, 4, 8], [2, 4, 7], [0, 2, 7], [2, 7, 9],
3870
+ [4, 9, 11], [4, 6, 9], [1, 3, 7], [2, 4, 9], [0, 5, 7], [0, 3, 10], [2, 9, 11],
3871
+ [0, 5, 10], [0, 6, 8], [4, 6, 10], [4, 6, 11], [1, 4, 11], [6, 8, 11],
3872
+ [1, 5, 11], [1, 6, 11], [1, 8, 10], [1, 6, 8], [3, 5, 8], [3, 8, 10],
3873
+ [1, 3, 8], [3, 5, 10], [1, 3, 6], [2, 5, 7, 10], [0, 3, 7, 10], [1, 4, 8, 11],
3874
+ [2, 4, 7, 11], [0, 4, 7, 9], [0, 2, 5, 9], [2, 6, 9, 11], [1, 5, 8, 10],
3875
+ [0, 3, 5, 8], [3, 6, 8, 11], [1, 3, 6, 10], [1, 4, 6, 9], [1, 5, 9], [0, 4, 8],
3876
+ [2, 6, 10], [3, 7, 11], [0, 3, 6, 9], [2, 5, 8, 11], [1, 4, 7, 10],
3877
+ [2, 5, 7, 11], [0, 2, 6, 9], [0, 4, 7, 10], [2, 4, 8, 11], [0, 3, 5, 9],
3878
+ [1, 4, 7, 9], [3, 6, 9, 11], [2, 5, 8, 10], [1, 4, 6, 10], [0, 3, 6, 8],
3879
+ [1, 3, 7, 10], [1, 5, 8, 11], [2, 4, 10], [5, 9, 11], [1, 5, 7], [0, 2, 8],
3880
+ [0, 4, 6], [1, 7, 11], [3, 7, 9], [1, 3, 9], [7, 9, 11], [5, 7, 9], [0, 6, 10],
3881
+ [0, 2, 10], [2, 6, 8], [0, 2, 4], [4, 8, 10], [1, 9, 11], [2, 4, 6],
3882
+ [3, 5, 11], [3, 5, 7], [0, 8, 10], [4, 6, 8], [1, 3, 11], [6, 8, 10],
3883
+ [1, 3, 5], [0, 2, 5, 10], [0, 5, 7, 9], [0, 3, 8, 10], [0, 2, 4, 7],
3884
+ [4, 6, 8, 11], [3, 5, 7, 10], [2, 7, 9, 11], [2, 4, 6, 9], [1, 6, 8, 10],
3885
+ [1, 4, 9, 11], [1, 3, 5, 8], [1, 3, 6, 11], [2, 5, 9, 11], [2, 4, 7, 10],
3886
+ [0, 2, 5, 8], [1, 5, 7, 10], [0, 4, 6, 9], [1, 3, 6, 9], [0, 3, 6, 10],
3887
+ [2, 6, 8, 11], [0, 2, 7, 9], [1, 4, 8, 10], [0, 3, 7, 9], [3, 5, 8, 11],
3888
+ [0, 5, 7, 10], [0, 2, 5, 7], [1, 4, 7, 11], [2, 4, 7, 9], [0, 3, 5, 10],
3889
+ [4, 6, 9, 11], [1, 4, 6, 11], [2, 4, 9, 11], [1, 6, 8, 11], [1, 3, 6, 8],
3890
+ [1, 3, 8, 10], [3, 5, 8, 10], [4, 7, 9, 11], [0, 2, 7, 10], [2, 5, 7, 9],
3891
+ [0, 2, 4, 9], [1, 6, 9, 11], [2, 4, 6, 11], [0, 3, 5, 7], [0, 5, 8, 10],
3892
+ [1, 4, 6, 8], [1, 3, 5, 10], [1, 3, 8, 11], [3, 6, 8, 10], [0, 2, 5, 7, 10],
3893
+ [0, 2, 4, 7, 9], [0, 2, 5, 7, 9], [1, 3, 7, 9], [1, 4, 6, 9, 11],
3894
+ [1, 3, 6, 8, 11], [3, 5, 9, 11], [1, 3, 6, 8, 10], [1, 4, 6, 8, 11],
3895
+ [1, 3, 5, 8, 10], [2, 4, 6, 9, 11], [2, 4, 8, 10], [2, 4, 7, 9, 11],
3896
+ [0, 3, 5, 7, 10], [1, 5, 7, 11], [0, 2, 6, 8], [0, 3, 5, 8, 10], [0, 4, 6, 10],
3897
+ [1, 3, 5, 9], [1, 5, 7, 9], [2, 6, 8, 10], [3, 7, 9, 11], [0, 2, 4, 8],
3898
+ [0, 4, 6, 8], [0, 4, 8, 10], [2, 4, 6, 10], [1, 3, 7, 11], [0, 2, 6, 10],
3899
+ [1, 5, 9, 11], [3, 5, 7, 11], [1, 7, 9, 11], [0, 2, 4, 6], [1, 3, 9, 11],
3900
+ [0, 2, 4, 10], [5, 7, 9, 11], [2, 4, 6, 8], [0, 2, 8, 10], [3, 5, 7, 9],
3901
+ [1, 3, 5, 7], [4, 6, 8, 10], [0, 6, 8, 10], [1, 3, 5, 11], [0, 3, 6, 8, 10],
3902
+ [0, 2, 4, 6, 9], [1, 4, 7, 9, 11], [2, 4, 6, 8, 11], [1, 3, 6, 9, 11],
3903
+ [1, 3, 5, 8, 11], [0, 2, 5, 8, 10], [1, 4, 6, 8, 10], [0, 3, 5, 7, 9],
3904
+ [2, 5, 7, 9, 11], [1, 3, 5, 7, 10], [0, 2, 4, 7, 10], [1, 3, 5, 7, 9],
3905
+ [1, 3, 5, 9, 11], [1, 5, 7, 9, 11], [1, 3, 7, 9, 11], [3, 5, 7, 9, 11],
3906
+ [2, 4, 6, 8, 10], [0, 4, 6, 8, 10], [0, 2, 6, 8, 10], [1, 3, 5, 7, 11],
3907
+ [0, 2, 4, 8, 10], [0, 2, 4, 6, 8], [0, 2, 4, 6, 10], [0, 2, 4, 6, 8, 10],
3908
+ [1, 3, 5, 7, 9, 11]]
3909
+
3910
+ def find_exact_match_variable_length(list_of_lists, target_list, uncertain_indices):
3911
+ # Infer possible values for each uncertain index
3912
+ possible_values = {idx: set() for idx in uncertain_indices}
3913
+ for sublist in list_of_lists:
3914
+ for idx in uncertain_indices:
3915
+ if idx < len(sublist):
3916
+ possible_values[idx].add(sublist[idx])
3917
+
3918
+ # Generate all possible combinations for the uncertain elements
3919
+ uncertain_combinations = product(*(possible_values[idx] for idx in uncertain_indices))
3920
+
3921
+ for combination in uncertain_combinations:
3922
+ # Create a copy of the target list and update the uncertain elements
3923
+ test_list = target_list[:]
3924
+ for idx, value in zip(uncertain_indices, combination):
3925
+ test_list[idx] = value
3926
+
3927
+ # Check if the modified target list is an exact match in the list of lists
3928
+ # Only consider sublists that are at least as long as the target list
3929
+ for sublist in list_of_lists:
3930
+ if len(sublist) >= len(test_list) and sublist[:len(test_list)] == test_list:
3931
+ return sublist # Return the matching sublist
3932
+
3933
+ return None # No exact match found
3934
+
3935
+
3936
+ def advanced_validate_chord_pitches(chord, channel_to_check = 0, return_sorted = True):
3937
+
3938
+ pitches_chord = sorted(list(set([x[4] for x in chord if 0 < x[4] < 128 and x[3] == channel_to_check])))
3939
+
3940
+ if pitches_chord:
3941
+
3942
+ tones_chord = sorted(list(set([c % 12 for c in sorted(list(set(pitches_chord)))])))
3943
+
3944
+ if not bad_chord(tones_chord):
3945
+ if return_sorted:
3946
+ chord.sort(key = lambda x: x[4], reverse=True)
3947
+ return chord
3948
+
3949
+ else:
3950
+ bad_chord_indices = list(set([i for s in [[tones_chord.index(a), tones_chord.index(b)] for a, b in zip(tones_chord, tones_chord[1:]) if b-a == 1] for i in s]))
3951
+
3952
+ good_tones_chord = find_exact_match_variable_length(ALL_CHORDS, tones_chord, bad_chord_indices)
3953
+
3954
+ if good_tones_chord is not None:
3955
+
3956
+ fixed_chord = []
3957
+
3958
+ for c in chord:
3959
+ if c[3] == channel_to_check:
3960
+ if (c[4] % 12) in good_tones_chord:
3961
+ fixed_chord.append(c)
3962
+ else:
3963
+ fixed_chord.append(c)
3964
+
3965
+ if return_sorted:
3966
+ fixed_chord.sort(key = lambda x: x[4], reverse=True)
3967
+
3968
+ else:
3969
+
3970
+ if 0 in tones_chord and 11 in tones_chord:
3971
+ tones_chord.remove(0)
3972
+
3973
+ fixed_tones = [[a, b] for a, b in zip(tones_chord, tones_chord[1:]) if b-a != 1]
3974
+
3975
+ fixed_tones_chord = []
3976
+ for f in fixed_tones:
3977
+ fixed_tones_chord.extend(f)
3978
+ fixed_tones_chord = list(set(fixed_tones_chord))
3979
+
3980
+ fixed_chord = []
3981
+
3982
+ for c in chord:
3983
+ if c[3] == channel_to_check:
3984
+ if (c[4] % 12) in fixed_tones_chord:
3985
+ fixed_chord.append(c)
3986
+ else:
3987
+ fixed_chord.append(c)
3988
+
3989
+ if return_sorted:
3990
+ fixed_chord.sort(key = lambda x: x[4], reverse=True)
3991
+
3992
+ return fixed_chord
3993
+
3994
+ else:
3995
+ chord.sort(key = lambda x: x[4], reverse=True)
3996
+ return chord
3997
+
3998
+ ###################################################################################
3999
+
4000
+ def analyze_score_pitches(score, channels_to_analyze=[0]):
4001
+
4002
+ analysis = {}
4003
+
4004
+ score_notes = [s for s in score if s[3] in channels_to_analyze]
4005
+
4006
+ cscore = chordify_score(score_notes)
4007
+
4008
+ chords_tones = []
4009
+
4010
+ all_tones = []
4011
+
4012
+ all_chords_good = True
4013
+
4014
+ bad_chords = []
4015
+
4016
+ for c in cscore:
4017
+ tones = sorted(list(set([t[4] % 12 for t in c])))
4018
+ chords_tones.append(tones)
4019
+ all_tones.extend(tones)
4020
+
4021
+ if tones not in ALL_CHORDS:
4022
+ all_chords_good = False
4023
+ bad_chords.append(tones)
4024
+
4025
+ analysis['Number of notes'] = len(score_notes)
4026
+ analysis['Number of chords'] = len(cscore)
4027
+ analysis['Score tones'] = sorted(list(set(all_tones)))
4028
+ analysis['Shortest chord'] = sorted(min(chords_tones, key=len))
4029
+ analysis['Longest chord'] = sorted(max(chords_tones, key=len))
4030
+ analysis['All chords good'] = all_chords_good
4031
+ analysis['Bad chords'] = bad_chords
4032
+
4033
+ return analysis
4034
+
4035
+ ###################################################################################
4036
+
4037
+ ALL_CHORDS_GROUPED = [[[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11]],
4038
+ [[0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8], [0, 9], [0, 10],
4039
+ [1, 3], [1, 4], [1, 5], [1, 6], [1, 7], [1, 8], [1, 9], [1, 10], [1, 11],
4040
+ [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [3, 5],
4041
+ [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [4, 6], [4, 7], [4, 8],
4042
+ [4, 9], [4, 10], [4, 11], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [6, 8],
4043
+ [6, 9], [6, 10], [6, 11], [7, 9], [7, 10], [7, 11], [8, 10], [8, 11],
4044
+ [9, 11]],
4045
+ [[0, 2, 4], [0, 2, 5], [0, 3, 5], [0, 2, 6], [0, 3, 6], [0, 4, 6], [0, 2, 7],
4046
+ [0, 3, 7], [0, 4, 7], [0, 5, 7], [0, 2, 8], [0, 3, 8], [0, 4, 8], [0, 5, 8],
4047
+ [0, 6, 8], [0, 2, 9], [0, 3, 9], [0, 4, 9], [0, 5, 9], [0, 6, 9], [0, 7, 9],
4048
+ [0, 2, 10], [0, 3, 10], [0, 4, 10], [0, 5, 10], [0, 6, 10], [0, 7, 10],
4049
+ [0, 8, 10], [1, 3, 5], [1, 3, 6], [1, 4, 6], [1, 3, 7], [1, 4, 7], [1, 5, 7],
4050
+ [1, 3, 8], [1, 4, 8], [1, 5, 8], [1, 6, 8], [1, 3, 9], [1, 4, 9], [1, 5, 9],
4051
+ [1, 6, 9], [1, 7, 9], [1, 3, 10], [1, 4, 10], [1, 5, 10], [1, 6, 10],
4052
+ [1, 7, 10], [1, 8, 10], [1, 3, 11], [1, 4, 11], [1, 5, 11], [1, 6, 11],
4053
+ [1, 7, 11], [1, 8, 11], [1, 9, 11], [2, 4, 6], [2, 4, 7], [2, 5, 7],
4054
+ [2, 4, 8], [2, 5, 8], [2, 6, 8], [2, 4, 9], [2, 5, 9], [2, 6, 9], [2, 7, 9],
4055
+ [2, 4, 10], [2, 5, 10], [2, 6, 10], [2, 7, 10], [2, 8, 10], [2, 4, 11],
4056
+ [2, 5, 11], [2, 6, 11], [2, 7, 11], [2, 8, 11], [2, 9, 11], [3, 5, 7],
4057
+ [3, 5, 8], [3, 6, 8], [3, 5, 9], [3, 6, 9], [3, 7, 9], [3, 5, 10], [3, 6, 10],
4058
+ [3, 7, 10], [3, 8, 10], [3, 5, 11], [3, 6, 11], [3, 7, 11], [3, 8, 11],
4059
+ [3, 9, 11], [4, 6, 8], [4, 6, 9], [4, 7, 9], [4, 6, 10], [4, 7, 10],
4060
+ [4, 8, 10], [4, 6, 11], [4, 7, 11], [4, 8, 11], [4, 9, 11], [5, 7, 9],
4061
+ [5, 7, 10], [5, 8, 10], [5, 7, 11], [5, 8, 11], [5, 9, 11], [6, 8, 10],
4062
+ [6, 8, 11], [6, 9, 11], [7, 9, 11]],
4063
+ [[0, 2, 4, 6], [0, 2, 4, 7], [0, 2, 5, 7], [0, 3, 5, 7], [0, 2, 4, 8],
4064
+ [0, 2, 5, 8], [0, 2, 6, 8], [0, 3, 5, 8], [0, 3, 6, 8], [0, 4, 6, 8],
4065
+ [0, 2, 4, 9], [0, 2, 5, 9], [0, 2, 6, 9], [0, 2, 7, 9], [0, 3, 5, 9],
4066
+ [0, 3, 6, 9], [0, 3, 7, 9], [0, 4, 6, 9], [0, 4, 7, 9], [0, 5, 7, 9],
4067
+ [0, 2, 4, 10], [0, 2, 5, 10], [0, 2, 6, 10], [0, 2, 7, 10], [0, 2, 8, 10],
4068
+ [0, 3, 5, 10], [0, 3, 6, 10], [0, 3, 7, 10], [0, 3, 8, 10], [0, 4, 6, 10],
4069
+ [0, 4, 7, 10], [0, 4, 8, 10], [0, 5, 7, 10], [0, 5, 8, 10], [0, 6, 8, 10],
4070
+ [1, 3, 5, 7], [1, 3, 5, 8], [1, 3, 6, 8], [1, 4, 6, 8], [1, 3, 5, 9],
4071
+ [1, 3, 6, 9], [1, 3, 7, 9], [1, 4, 6, 9], [1, 4, 7, 9], [1, 5, 7, 9],
4072
+ [1, 3, 5, 10], [1, 3, 6, 10], [1, 3, 7, 10], [1, 3, 8, 10], [1, 4, 6, 10],
4073
+ [1, 4, 7, 10], [1, 4, 8, 10], [1, 5, 7, 10], [1, 5, 8, 10], [1, 6, 8, 10],
4074
+ [1, 3, 5, 11], [1, 3, 6, 11], [1, 3, 7, 11], [1, 3, 8, 11], [1, 3, 9, 11],
4075
+ [1, 4, 6, 11], [1, 4, 7, 11], [1, 4, 8, 11], [1, 4, 9, 11], [1, 5, 7, 11],
4076
+ [1, 5, 8, 11], [1, 5, 9, 11], [1, 6, 8, 11], [1, 6, 9, 11], [1, 7, 9, 11],
4077
+ [2, 4, 6, 8], [2, 4, 6, 9], [2, 4, 7, 9], [2, 5, 7, 9], [2, 4, 6, 10],
4078
+ [2, 4, 7, 10], [2, 4, 8, 10], [2, 5, 7, 10], [2, 5, 8, 10], [2, 6, 8, 10],
4079
+ [2, 4, 6, 11], [2, 4, 7, 11], [2, 4, 8, 11], [2, 4, 9, 11], [2, 5, 7, 11],
4080
+ [2, 5, 8, 11], [2, 5, 9, 11], [2, 6, 8, 11], [2, 6, 9, 11], [2, 7, 9, 11],
4081
+ [3, 5, 7, 9], [3, 5, 7, 10], [3, 5, 8, 10], [3, 6, 8, 10], [3, 5, 7, 11],
4082
+ [3, 5, 8, 11], [3, 5, 9, 11], [3, 6, 8, 11], [3, 6, 9, 11], [3, 7, 9, 11],
4083
+ [4, 6, 8, 10], [4, 6, 8, 11], [4, 6, 9, 11], [4, 7, 9, 11], [5, 7, 9, 11]],
4084
+ [[0, 2, 4, 6, 8], [0, 2, 4, 6, 9], [0, 2, 4, 7, 9], [0, 2, 5, 7, 9],
4085
+ [0, 3, 5, 7, 9], [0, 2, 4, 6, 10], [0, 2, 4, 7, 10], [0, 2, 4, 8, 10],
4086
+ [0, 2, 5, 7, 10], [0, 2, 5, 8, 10], [0, 2, 6, 8, 10], [0, 3, 5, 7, 10],
4087
+ [0, 3, 5, 8, 10], [0, 3, 6, 8, 10], [0, 4, 6, 8, 10], [1, 3, 5, 7, 9],
4088
+ [1, 3, 5, 7, 10], [1, 3, 5, 8, 10], [1, 3, 6, 8, 10], [1, 4, 6, 8, 10],
4089
+ [1, 3, 5, 7, 11], [1, 3, 5, 8, 11], [1, 3, 5, 9, 11], [1, 3, 6, 8, 11],
4090
+ [1, 3, 6, 9, 11], [1, 3, 7, 9, 11], [1, 4, 6, 8, 11], [1, 4, 6, 9, 11],
4091
+ [1, 4, 7, 9, 11], [1, 5, 7, 9, 11], [2, 4, 6, 8, 10], [2, 4, 6, 8, 11],
4092
+ [2, 4, 6, 9, 11], [2, 4, 7, 9, 11], [2, 5, 7, 9, 11], [3, 5, 7, 9, 11]],
4093
+ [[0, 2, 4, 6, 8, 10], [1, 3, 5, 7, 9, 11]]]
4094
+
4095
+ def group_sublists_by_length(lst):
4096
+ unique_lengths = sorted(list(set(map(len, lst))), reverse=True)
4097
+ return [[x for x in lst if len(x) == i] for i in unique_lengths]
4098
+
4099
+ def pitches_to_tones_chord(pitches):
4100
+ return sorted(set([p % 12 for p in pitches]))
4101
+
4102
+ def tones_chord_to_pitches(tones_chord, base_pitch=60):
4103
+ return [t+base_pitch for t in tones_chord if 0 <= t < 12]
4104
+
4105
+ ###################################################################################
4106
+
4107
+ def advanced_score_processor(raw_score,
4108
+ patches_to_analyze=list(range(129)),
4109
+ return_score_analysis=False,
4110
+ return_enhanced_score=False,
4111
+ return_enhanced_score_notes=False,
4112
+ return_enhanced_monophonic_melody=False,
4113
+ return_chordified_enhanced_score=False,
4114
+ return_chordified_enhanced_score_with_lyrics=False,
4115
+ return_score_tones_chords=False,
4116
+ return_text_and_lyric_events=False
4117
+ ):
4118
+
4119
+ '''TMIDIX Advanced Score Processor'''
4120
+
4121
+ # Score data types detection
4122
+
4123
+ if raw_score and type(raw_score) == list:
4124
+
4125
+ num_ticks = 0
4126
+ num_tracks = 1
4127
+
4128
+ basic_single_track_score = []
4129
+
4130
+ if type(raw_score[0]) != int:
4131
+ if len(raw_score[0]) < 5 and type(raw_score[0][0]) != str:
4132
+ return ['Check score for errors and compatibility!']
4133
+
4134
+ else:
4135
+ basic_single_track_score = copy.deepcopy(raw_score)
4136
+
4137
+ else:
4138
+ num_ticks = raw_score[0]
4139
+ while num_tracks < len(raw_score):
4140
+ for event in raw_score[num_tracks]:
4141
+ ev = copy.deepcopy(event)
4142
+ basic_single_track_score.append(ev)
4143
+ num_tracks += 1
4144
+
4145
+ basic_single_track_score.sort(key=lambda x: x[4] if x[0] == 'note' else 128, reverse=True)
4146
+ basic_single_track_score.sort(key=lambda x: x[1])
4147
+
4148
+ enhanced_single_track_score = []
4149
+ patches = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
4150
+ all_score_patches = []
4151
+ num_patch_changes = 0
4152
+
4153
+ for event in basic_single_track_score:
4154
+ if event[0] == 'patch_change':
4155
+ patches[event[2]] = event[3]
4156
+ enhanced_single_track_score.append(event)
4157
+ num_patch_changes += 1
4158
+
4159
+ if event[0] == 'note':
4160
+ if event[3] != 9:
4161
+ event.extend([patches[event[3]]])
4162
+ all_score_patches.extend([patches[event[3]]])
4163
+ else:
4164
+ event.extend([128])
4165
+ all_score_patches.extend([128])
4166
+
4167
+ if enhanced_single_track_score:
4168
+ if (event[1] == enhanced_single_track_score[-1][1]):
4169
+ if ([event[3], event[4]] != enhanced_single_track_score[-1][3:5]):
4170
+ enhanced_single_track_score.append(event)
4171
+ else:
4172
+ enhanced_single_track_score.append(event)
4173
+
4174
+ else:
4175
+ enhanced_single_track_score.append(event)
4176
+
4177
+ if event[0] not in ['note', 'patch_change']:
4178
+ enhanced_single_track_score.append(event)
4179
+
4180
+ enhanced_single_track_score.sort(key=lambda x: x[6] if x[0] == 'note' else -1)
4181
+ enhanced_single_track_score.sort(key=lambda x: x[4] if x[0] == 'note' else 128, reverse=True)
4182
+ enhanced_single_track_score.sort(key=lambda x: x[1])
4183
+
4184
+ # Analysis and chordification
4185
+
4186
+ cscore = []
4187
+ cescore = []
4188
+ chords_tones = []
4189
+ tones_chords = []
4190
+ all_tones = []
4191
+ all_chords_good = True
4192
+ bad_chords = []
4193
+ bad_chords_count = 0
4194
+ score_notes = []
4195
+ score_pitches = []
4196
+ score_patches = []
4197
+ num_text_events = 0
4198
+ num_lyric_events = 0
4199
+ num_other_events = 0
4200
+ text_and_lyric_events = []
4201
+ text_and_lyric_events_latin = None
4202
+
4203
+ analysis = {}
4204
+
4205
+ score_notes = [s for s in enhanced_single_track_score if s[0] == 'note' and s[6] in patches_to_analyze]
4206
+ score_patches = [sn[6] for sn in score_notes]
4207
+
4208
+ if return_text_and_lyric_events:
4209
+ text_and_lyric_events = [e for e in enhanced_single_track_score if e[0] in ['text_event', 'lyric']]
4210
+
4211
+ if text_and_lyric_events:
4212
+ text_and_lyric_events_latin = True
4213
+ for e in text_and_lyric_events:
4214
+ try:
4215
+ tle = str(e[2].decode())
4216
+ except:
4217
+ tle = str(e[2])
4218
+
4219
+ for c in tle:
4220
+ if not 0 <= ord(c) < 128:
4221
+ text_and_lyric_events_latin = False
4222
+
4223
+ if (return_chordified_enhanced_score or return_score_analysis) and any(elem in patches_to_analyze for elem in score_patches):
4224
+
4225
+ cescore = chordify_score([num_ticks, enhanced_single_track_score])
4226
+
4227
+ if return_score_analysis:
4228
+
4229
+ cscore = chordify_score(score_notes)
4230
+
4231
+ score_pitches = [sn[4] for sn in score_notes]
4232
+
4233
+ text_events = [e for e in enhanced_single_track_score if e[0] == 'text_event']
4234
+ num_text_events = len(text_events)
4235
+
4236
+ lyric_events = [e for e in enhanced_single_track_score if e[0] == 'lyric']
4237
+ num_lyric_events = len(lyric_events)
4238
+
4239
+ other_events = [e for e in enhanced_single_track_score if e[0] not in ['note', 'patch_change', 'text_event', 'lyric']]
4240
+ num_other_events = len(other_events)
4241
+
4242
+ for c in cscore:
4243
+ tones = sorted(set([t[4] % 12 for t in c if t[3] != 9]))
4244
+
4245
+ if tones:
4246
+ chords_tones.append(tones)
4247
+ all_tones.extend(tones)
4248
+
4249
+ if tones not in ALL_CHORDS:
4250
+ all_chords_good = False
4251
+ bad_chords.append(tones)
4252
+ bad_chords_count += 1
4253
+
4254
+ analysis['Number of ticks per quarter note'] = num_ticks
4255
+ analysis['Number of tracks'] = num_tracks
4256
+ analysis['Number of all events'] = len(enhanced_single_track_score)
4257
+ analysis['Number of patch change events'] = num_patch_changes
4258
+ analysis['Number of text events'] = num_text_events
4259
+ analysis['Number of lyric events'] = num_lyric_events
4260
+ analysis['All text and lyric events Latin'] = text_and_lyric_events_latin
4261
+ analysis['Number of other events'] = num_other_events
4262
+ analysis['Number of score notes'] = len(score_notes)
4263
+ analysis['Number of score chords'] = len(cscore)
4264
+ analysis['Score patches'] = sorted(set(score_patches))
4265
+ analysis['Score pitches'] = sorted(set(score_pitches))
4266
+ analysis['Score tones'] = sorted(set(all_tones))
4267
+ if chords_tones:
4268
+ analysis['Shortest chord'] = sorted(min(chords_tones, key=len))
4269
+ analysis['Longest chord'] = sorted(max(chords_tones, key=len))
4270
+ analysis['All chords good'] = all_chords_good
4271
+ analysis['Number of bad chords'] = bad_chords_count
4272
+ analysis['Bad chords'] = sorted([list(c) for c in set(tuple(bc) for bc in bad_chords)])
4273
+
4274
+ else:
4275
+ analysis['Error'] = 'Provided score does not have specified patches to analyse'
4276
+ analysis['Provided patches to analyse'] = sorted(patches_to_analyze)
4277
+ analysis['Patches present in the score'] = sorted(set(all_score_patches))
4278
+
4279
+ if return_enhanced_monophonic_melody:
4280
+
4281
+ score_notes_copy = copy.deepcopy(score_notes)
4282
+ chordified_score_notes = chordify_score(score_notes_copy)
4283
+
4284
+ melody = [c[0] for c in chordified_score_notes]
4285
+
4286
+ fixed_melody = []
4287
+
4288
+ for i in range(len(melody)-1):
4289
+ note = melody[i]
4290
+ nmt = melody[i+1][1]
4291
+
4292
+ if note[1]+note[2] >= nmt:
4293
+ note_dur = nmt-note[1]-1
4294
+ else:
4295
+ note_dur = note[2]
4296
+
4297
+ melody[i][2] = note_dur
4298
+
4299
+ fixed_melody.append(melody[i])
4300
+ fixed_melody.append(melody[-1])
4301
+
4302
+ if return_score_tones_chords:
4303
+ cscore = chordify_score(score_notes)
4304
+ for c in cscore:
4305
+ tones_chord = sorted(set([t[4] % 12 for t in c if t[3] != 9]))
4306
+ if tones_chord:
4307
+ tones_chords.append(tones_chord)
4308
+
4309
+ if return_chordified_enhanced_score_with_lyrics:
4310
+ score_with_lyrics = [e for e in enhanced_single_track_score if e[0] in ['note', 'text_event', 'lyric']]
4311
+ chordified_enhanced_score_with_lyrics = chordify_score(score_with_lyrics)
4312
+
4313
+ # Returned data
4314
+
4315
+ requested_data = []
4316
+
4317
+ if return_score_analysis and analysis:
4318
+ requested_data.append([[k, v] for k, v in analysis.items()])
4319
+
4320
+ if return_enhanced_score and enhanced_single_track_score:
4321
+ requested_data.append([num_ticks, enhanced_single_track_score])
4322
+
4323
+ if return_enhanced_score_notes and score_notes:
4324
+ requested_data.append(score_notes)
4325
+
4326
+ if return_enhanced_monophonic_melody and fixed_melody:
4327
+ requested_data.append(fixed_melody)
4328
+
4329
+ if return_chordified_enhanced_score and cescore:
4330
+ requested_data.append(cescore)
4331
+
4332
+ if return_chordified_enhanced_score_with_lyrics and chordified_enhanced_score_with_lyrics:
4333
+ requested_data.append(chordified_enhanced_score_with_lyrics)
4334
+
4335
+ if return_score_tones_chords and tones_chords:
4336
+ requested_data.append(tones_chords)
4337
+
4338
+ if return_text_and_lyric_events and text_and_lyric_events:
4339
+ requested_data.append(text_and_lyric_events)
4340
+
4341
+ return requested_data
4342
+
4343
+ else:
4344
+ return ['Check score for errors and compatibility!']
4345
+
4346
+ ###################################################################################
4347
+
4348
+ import random
4349
+ import copy
4350
+
4351
+ ###################################################################################
4352
+
4353
+ def replace_bad_tones_chord(bad_tones_chord):
4354
+ bad_chord_p = [0] * 12
4355
+ for b in bad_tones_chord:
4356
+ bad_chord_p[b] = 1
4357
+
4358
+ match_ratios = []
4359
+ good_chords = []
4360
+ for c in ALL_CHORDS:
4361
+ good_chord_p = [0] * 12
4362
+ for cc in c:
4363
+ good_chord_p[cc] = 1
4364
+
4365
+ good_chords.append(good_chord_p)
4366
+ match_ratios.append(sum(i == j for i, j in zip(good_chord_p, bad_chord_p)) / len(good_chord_p))
4367
+
4368
+ best_good_chord = good_chords[match_ratios.index(max(match_ratios))]
4369
+
4370
+ replaced_chord = []
4371
+ for i in range(len(best_good_chord)):
4372
+ if best_good_chord[i] == 1:
4373
+ replaced_chord.append(i)
4374
+
4375
+ return [replaced_chord, max(match_ratios)]
4376
+
4377
+ ###################################################################################
4378
+
4379
+ def check_and_fix_chord(chord,
4380
+ channel_index=3,
4381
+ pitch_index=4
4382
+ ):
4383
+
4384
+ tones_chord = sorted(set([t[pitch_index] % 12 for t in chord if t[channel_index] != 9]))
4385
+
4386
+ notes_events = [t for t in chord if t[channel_index] != 9]
4387
+ notes_events.sort(key=lambda x: x[pitch_index], reverse=True)
4388
+
4389
+ drums_events = [t for t in chord if t[channel_index] == 9]
4390
+
4391
+ checked_and_fixed_chord = []
4392
+
4393
+ if tones_chord:
4394
+
4395
+ new_tones_chord = advanced_check_and_fix_tones_chord(tones_chord, high_pitch=notes_events[0][pitch_index])
4396
+
4397
+ if new_tones_chord != tones_chord:
4398
+
4399
+ if len(notes_events) > 1:
4400
+ checked_and_fixed_chord.extend([notes_events[0]])
4401
+ for cc in notes_events[1:]:
4402
+ if cc[channel_index] != 9:
4403
+ if (cc[pitch_index] % 12) in new_tones_chord:
4404
+ checked_and_fixed_chord.extend([cc])
4405
+ checked_and_fixed_chord.extend(drums_events)
4406
+ else:
4407
+ checked_and_fixed_chord.extend([notes_events[0]])
4408
+ else:
4409
+ checked_and_fixed_chord.extend(chord)
4410
+ else:
4411
+ checked_and_fixed_chord.extend(chord)
4412
+
4413
+ checked_and_fixed_chord.sort(key=lambda x: x[pitch_index], reverse=True)
4414
+
4415
+ return checked_and_fixed_chord
4416
+
4417
+ ###################################################################################
4418
+
4419
+ def find_similar_tones_chord(tones_chord,
4420
+ max_match_threshold=1,
4421
+ randomize_chords_matches=False,
4422
+ custom_chords_list=[]):
4423
+ chord_p = [0] * 12
4424
+ for b in tones_chord:
4425
+ chord_p[b] = 1
4426
+
4427
+ match_ratios = []
4428
+ good_chords = []
4429
+
4430
+ if custom_chords_list:
4431
+ CHORDS = copy.deepcopy([list(x) for x in set(tuple(t) for t in custom_chords_list)])
4432
+ else:
4433
+ CHORDS = copy.deepcopy(ALL_CHORDS)
4434
+
4435
+ if randomize_chords_matches:
4436
+ random.shuffle(CHORDS)
4437
+
4438
+ for c in CHORDS:
4439
+ good_chord_p = [0] * 12
4440
+ for cc in c:
4441
+ good_chord_p[cc] = 1
4442
+
4443
+ good_chords.append(good_chord_p)
4444
+ match_ratio = sum(i == j for i, j in zip(good_chord_p, chord_p)) / len(good_chord_p)
4445
+ if match_ratio < max_match_threshold:
4446
+ match_ratios.append(match_ratio)
4447
+ else:
4448
+ match_ratios.append(0)
4449
+
4450
+ best_good_chord = good_chords[match_ratios.index(max(match_ratios))]
4451
+
4452
+ similar_chord = []
4453
+ for i in range(len(best_good_chord)):
4454
+ if best_good_chord[i] == 1:
4455
+ similar_chord.append(i)
4456
+
4457
+ return [similar_chord, max(match_ratios)]
4458
+
4459
+ ###################################################################################
4460
+
4461
+ def generate_tones_chords_progression(number_of_chords_to_generate=100,
4462
+ start_tones_chord=[],
4463
+ custom_chords_list=[]):
4464
+
4465
+ if start_tones_chord:
4466
+ start_chord = start_tones_chord
4467
+ else:
4468
+ start_chord = random.choice(ALL_CHORDS)
4469
+
4470
+ chord = []
4471
+
4472
+ chords_progression = [start_chord]
4473
+
4474
+ for i in range(number_of_chords_to_generate):
4475
+ if not chord:
4476
+ chord = start_chord
4477
+
4478
+ if custom_chords_list:
4479
+ chord = find_similar_tones_chord(chord, randomize_chords_matches=True, custom_chords_list=custom_chords_list)[0]
4480
+ else:
4481
+ chord = find_similar_tones_chord(chord, randomize_chords_matches=True)[0]
4482
+
4483
+ chords_progression.append(chord)
4484
+
4485
+ return chords_progression
4486
+
4487
+ ###################################################################################
4488
+
4489
+ def ascii_texts_search(texts = ['text1', 'text2', 'text3'],
4490
+ search_query = 'Once upon a time...',
4491
+ deterministic_matching = False
4492
+ ):
4493
+
4494
+ texts_copy = texts
4495
+
4496
+ if not deterministic_matching:
4497
+ texts_copy = copy.deepcopy(texts)
4498
+ random.shuffle(texts_copy)
4499
+
4500
+ clean_texts = []
4501
+
4502
+ for t in texts_copy:
4503
+ text_words_list = [at.split(chr(32)) for at in t.split(chr(10))]
4504
+
4505
+ clean_text_words_list = []
4506
+ for twl in text_words_list:
4507
+ for w in twl:
4508
+ clean_text_words_list.append(''.join(filter(str.isalpha, w.lower())))
4509
+
4510
+ clean_texts.append(clean_text_words_list)
4511
+
4512
+ text_search_query = [at.split(chr(32)) for at in search_query.split(chr(10))]
4513
+ clean_text_search_query = []
4514
+ for w in text_search_query:
4515
+ for ww in w:
4516
+ clean_text_search_query.append(''.join(filter(str.isalpha, ww.lower())))
4517
+
4518
+ if clean_texts[0] and clean_text_search_query:
4519
+ texts_match_ratios = []
4520
+ words_match_indexes = []
4521
+ for t in clean_texts:
4522
+ word_match_count = 0
4523
+ wmis = []
4524
+
4525
+ for c in clean_text_search_query:
4526
+ if c in t:
4527
+ word_match_count += 1
4528
+ wmis.append(t.index(c))
4529
+ else:
4530
+ wmis.append(-1)
4531
+
4532
+ words_match_indexes.append(wmis)
4533
+ words_match_indexes_consequtive = all(abs(b) - abs(a) == 1 for a, b in zip(wmis, wmis[1:]))
4534
+ words_match_indexes_consequtive_ratio = sum([abs(b) - abs(a) == 1 for a, b in zip(wmis, wmis[1:])]) / len(wmis)
4535
+
4536
+ if words_match_indexes_consequtive:
4537
+ texts_match_ratios.append(word_match_count / len(clean_text_search_query))
4538
+ else:
4539
+ texts_match_ratios.append(((word_match_count / len(clean_text_search_query)) + words_match_indexes_consequtive_ratio) / 2)
4540
+
4541
+ if texts_match_ratios:
4542
+ max_text_match_ratio = max(texts_match_ratios)
4543
+ max_match_ratio_text = texts_copy[texts_match_ratios.index(max_text_match_ratio)]
4544
+ max_text_words_match_indexes = words_match_indexes[texts_match_ratios.index(max_text_match_ratio)]
4545
+
4546
+ return [max_match_ratio_text, max_text_match_ratio, max_text_words_match_indexes]
4547
+
4548
+ else:
4549
+ return None
4550
+
4551
+ ###################################################################################
4552
+
4553
+ def ascii_text_words_counter(ascii_text):
4554
+
4555
+ text_words_list = [at.split(chr(32)) for at in ascii_text.split(chr(10))]
4556
+
4557
+ clean_text_words_list = []
4558
+ for twl in text_words_list:
4559
+ for w in twl:
4560
+ wo = ''
4561
+ for ww in w.lower():
4562
+ if 96 < ord(ww) < 123:
4563
+ wo += ww
4564
+ if wo != '':
4565
+ clean_text_words_list.append(wo)
4566
+
4567
+ words = {}
4568
+ for i in clean_text_words_list:
4569
+ words[i] = words.get(i, 0) + 1
4570
+
4571
+ words_sorted = dict(sorted(words.items(), key=lambda item: item[1], reverse=True))
4572
+
4573
+ return len(clean_text_words_list), words_sorted, clean_text_words_list
4574
+
4575
+ ###################################################################################
4576
+
4577
+ def check_and_fix_tones_chord(tones_chord):
4578
+
4579
+ lst = tones_chord
4580
+
4581
+ if len(lst) == 2:
4582
+ if lst[1] - lst[0] == 1:
4583
+ return [lst[-1]]
4584
+ else:
4585
+ if 0 in lst and 11 in lst:
4586
+ lst.remove(0)
4587
+ return lst
4588
+
4589
+ non_consecutive = [lst[0]]
4590
+
4591
+ if len(lst) > 2:
4592
+ for i in range(1, len(lst) - 1):
4593
+ if lst[i-1] + 1 != lst[i] and lst[i] + 1 != lst[i+1]:
4594
+ non_consecutive.append(lst[i])
4595
+ non_consecutive.append(lst[-1])
4596
+
4597
+ if 0 in non_consecutive and 11 in non_consecutive:
4598
+ non_consecutive.remove(0)
4599
+
4600
+ return non_consecutive
4601
+
4602
+ ###################################################################################
4603
+
4604
+ def find_closest_tone(tones, tone):
4605
+ return min(tones, key=lambda x:abs(x-tone))
4606
+
4607
+ def advanced_check_and_fix_tones_chord(tones_chord, high_pitch=0):
4608
+
4609
+ lst = tones_chord
4610
+
4611
+ if 0 < high_pitch < 128:
4612
+ ht = high_pitch % 12
4613
+ else:
4614
+ ht = 12
4615
+
4616
+ cht = find_closest_tone(lst, ht)
4617
+
4618
+ if len(lst) == 2:
4619
+ if lst[1] - lst[0] == 1:
4620
+ return [cht]
4621
+ else:
4622
+ if 0 in lst and 11 in lst:
4623
+ if find_closest_tone([0, 11], cht) == 11:
4624
+ lst.remove(0)
4625
+ else:
4626
+ lst.remove(11)
4627
+ return lst
4628
+
4629
+ non_consecutive = []
4630
+
4631
+ if len(lst) > 2:
4632
+ for i in range(0, len(lst) - 1):
4633
+ if lst[i] + 1 != lst[i+1]:
4634
+ non_consecutive.append(lst[i])
4635
+ if lst[-1] - lst[-2] > 1:
4636
+ non_consecutive.append(lst[-1])
4637
+
4638
+ if cht not in non_consecutive:
4639
+ non_consecutive.append(cht)
4640
+ non_consecutive.sort()
4641
+ if any(abs(non_consecutive[i+1] - non_consecutive[i]) == 1 for i in range(len(non_consecutive) - 1)):
4642
+ final_list = [x for x in non_consecutive if x == cht or abs(x - cht) > 1]
4643
+ else:
4644
+ final_list = non_consecutive
4645
+
4646
+ else:
4647
+ final_list = non_consecutive
4648
+
4649
+ if 0 in final_list and 11 in final_list:
4650
+ if find_closest_tone([0, 11], cht) == 11:
4651
+ final_list.remove(0)
4652
+ else:
4653
+ final_list.remove(11)
4654
+
4655
+ if cht in final_list or ht in final_list:
4656
+ return final_list
4657
+ else:
4658
+ return ['Error']
4659
+
4660
+ ###################################################################################
4661
+
4662
+ def create_similarity_matrix(list_of_values, matrix_length=0):
4663
+
4664
+ counts = Counter(list_of_values).items()
4665
+
4666
+ if matrix_length > 0:
4667
+ sim_matrix = [0] * max(matrix_length, len(list_of_values))
4668
+ else:
4669
+ sim_matrix = [0] * len(counts)
4670
+
4671
+ for c in counts:
4672
+ sim_matrix[c[0]] = c[1]
4673
+
4674
+ similarity_matrix = [[0] * len(sim_matrix) for _ in range(len(sim_matrix))]
4675
+
4676
+ for i in range(len(sim_matrix)):
4677
+ for j in range(len(sim_matrix)):
4678
+ if max(sim_matrix[i], sim_matrix[j]) != 0:
4679
+ similarity_matrix[i][j] = min(sim_matrix[i], sim_matrix[j]) / max(sim_matrix[i], sim_matrix[j])
4680
+
4681
+ return similarity_matrix, sim_matrix
4682
+
4683
+ ###################################################################################
4684
+
4685
+ def augment_enhanced_score_notes(enhanced_score_notes,
4686
+ timings_divider=16,
4687
+ full_sorting=True,
4688
+ timings_shift=0,
4689
+ pitch_shift=0
4690
+ ):
4691
+
4692
+ esn = copy.deepcopy(enhanced_score_notes)
4693
+
4694
+ for e in esn:
4695
+ e[1] = int(e[1] / timings_divider) + timings_shift
4696
+ e[2] = int(e[2] / timings_divider) + timings_shift
4697
+ e[4] = e[4] + pitch_shift
4698
+
4699
+ if full_sorting:
4700
+
4701
+ # Sorting by patch, pitch, then by start-time
4702
+ esn.sort(key=lambda x: x[6])
4703
+ esn.sort(key=lambda x: x[4], reverse=True)
4704
+ esn.sort(key=lambda x: x[1])
4705
+
4706
+ return esn
4707
+
4708
+ ###################################################################################
4709
+
4710
+ def stack_list(lst, base=12):
4711
+ return sum(j * base**i for i, j in enumerate(lst[::-1]))
4712
+
4713
+ def destack_list(num, base=12):
4714
+ lst = []
4715
+ while num:
4716
+ lst.append(num % base)
4717
+ num //= base
4718
+ return lst[::-1]
4719
+
4720
+ ###################################################################################
4721
+
4722
+ def extract_melody(chordified_enhanced_score,
4723
+ melody_range=[48, 84],
4724
+ melody_channel=0,
4725
+ melody_patch=0,
4726
+ melody_velocity=0,
4727
+ stacked_melody=False,
4728
+ stacked_melody_base_pitch=60
4729
+ ):
4730
+
4731
+ if stacked_melody:
4732
+
4733
+
4734
+ all_pitches_chords = []
4735
+ for e in chordified_enhanced_score:
4736
+ all_pitches_chords.append(sorted(set([p[4] for p in e]), reverse=True))
4737
+
4738
+ melody_score = []
4739
+ for i, chord in enumerate(chordified_enhanced_score):
4740
+
4741
+ if melody_velocity > 0:
4742
+ vel = melody_velocity
4743
+ else:
4744
+ vel = chord[0][5]
4745
+
4746
+ melody_score.append(['note', chord[0][1], chord[0][2], melody_channel, stacked_melody_base_pitch+(stack_list([p % 12 for p in all_pitches_chords[i]]) % 12), vel, melody_patch])
4747
+
4748
+ else:
4749
+
4750
+ melody_score = copy.deepcopy([c[0] for c in chordified_enhanced_score if c[0][3] != 9])
4751
+
4752
+ for e in melody_score:
4753
+
4754
+ e[3] = melody_channel
4755
+
4756
+ if melody_velocity > 0:
4757
+ e[5] = melody_velocity
4758
+
4759
+ e[6] = melody_patch
4760
+
4761
+ if e[4] < melody_range[0]:
4762
+ e[4] = (e[4] % 12) + melody_range[0]
4763
+
4764
+ if e[4] >= melody_range[1]:
4765
+ e[4] = (e[4] % 12) + (melody_range[1]-12)
4766
+
4767
+ return fix_monophonic_score_durations(melody_score)
4768
+
4769
+ ###################################################################################
4770
+
4771
+ def flip_enhanced_score_notes(enhanced_score_notes):
4772
+
4773
+ min_pitch = min([e[4] for e in enhanced_score_notes if e[3] != 9])
4774
+
4775
+ fliped_score_pitches = [127 - e[4]for e in enhanced_score_notes if e[3] != 9]
4776
+
4777
+ delta_min_pitch = min_pitch - min([p for p in fliped_score_pitches])
4778
+
4779
+ output_score = copy.deepcopy(enhanced_score_notes)
4780
+
4781
+ for e in output_score:
4782
+ if e[3] != 9:
4783
+ e[4] = (127 - e[4]) + delta_min_pitch
4784
+
4785
+ return output_score
4786
+
4787
+ ###################################################################################
4788
+
4789
+ ALL_CHORDS_SORTED = [[0], [0, 2], [0, 3], [0, 4], [0, 2, 4], [0, 5], [0, 2, 5], [0, 3, 5], [0, 6],
4790
+ [0, 2, 6], [0, 3, 6], [0, 4, 6], [0, 2, 4, 6], [0, 7], [0, 2, 7], [0, 3, 7],
4791
+ [0, 4, 7], [0, 5, 7], [0, 2, 4, 7], [0, 2, 5, 7], [0, 3, 5, 7], [0, 8],
4792
+ [0, 2, 8], [0, 3, 8], [0, 4, 8], [0, 5, 8], [0, 6, 8], [0, 2, 4, 8],
4793
+ [0, 2, 5, 8], [0, 2, 6, 8], [0, 3, 5, 8], [0, 3, 6, 8], [0, 4, 6, 8],
4794
+ [0, 2, 4, 6, 8], [0, 9], [0, 2, 9], [0, 3, 9], [0, 4, 9], [0, 5, 9], [0, 6, 9],
4795
+ [0, 7, 9], [0, 2, 4, 9], [0, 2, 5, 9], [0, 2, 6, 9], [0, 2, 7, 9],
4796
+ [0, 3, 5, 9], [0, 3, 6, 9], [0, 3, 7, 9], [0, 4, 6, 9], [0, 4, 7, 9],
4797
+ [0, 5, 7, 9], [0, 2, 4, 6, 9], [0, 2, 4, 7, 9], [0, 2, 5, 7, 9],
4798
+ [0, 3, 5, 7, 9], [0, 10], [0, 2, 10], [0, 3, 10], [0, 4, 10], [0, 5, 10],
4799
+ [0, 6, 10], [0, 7, 10], [0, 8, 10], [0, 2, 4, 10], [0, 2, 5, 10],
4800
+ [0, 2, 6, 10], [0, 2, 7, 10], [0, 2, 8, 10], [0, 3, 5, 10], [0, 3, 6, 10],
4801
+ [0, 3, 7, 10], [0, 3, 8, 10], [0, 4, 6, 10], [0, 4, 7, 10], [0, 4, 8, 10],
4802
+ [0, 5, 7, 10], [0, 5, 8, 10], [0, 6, 8, 10], [0, 2, 4, 6, 10],
4803
+ [0, 2, 4, 7, 10], [0, 2, 4, 8, 10], [0, 2, 5, 7, 10], [0, 2, 5, 8, 10],
4804
+ [0, 2, 6, 8, 10], [0, 3, 5, 7, 10], [0, 3, 5, 8, 10], [0, 3, 6, 8, 10],
4805
+ [0, 4, 6, 8, 10], [0, 2, 4, 6, 8, 10], [1], [1, 3], [1, 4], [1, 5], [1, 3, 5],
4806
+ [1, 6], [1, 3, 6], [1, 4, 6], [1, 7], [1, 3, 7], [1, 4, 7], [1, 5, 7],
4807
+ [1, 3, 5, 7], [1, 8], [1, 3, 8], [1, 4, 8], [1, 5, 8], [1, 6, 8], [1, 3, 5, 8],
4808
+ [1, 3, 6, 8], [1, 4, 6, 8], [1, 9], [1, 3, 9], [1, 4, 9], [1, 5, 9], [1, 6, 9],
4809
+ [1, 7, 9], [1, 3, 5, 9], [1, 3, 6, 9], [1, 3, 7, 9], [1, 4, 6, 9],
4810
+ [1, 4, 7, 9], [1, 5, 7, 9], [1, 3, 5, 7, 9], [1, 10], [1, 3, 10], [1, 4, 10],
4811
+ [1, 5, 10], [1, 6, 10], [1, 7, 10], [1, 8, 10], [1, 3, 5, 10], [1, 3, 6, 10],
4812
+ [1, 3, 7, 10], [1, 3, 8, 10], [1, 4, 6, 10], [1, 4, 7, 10], [1, 4, 8, 10],
4813
+ [1, 5, 7, 10], [1, 5, 8, 10], [1, 6, 8, 10], [1, 3, 5, 7, 10],
4814
+ [1, 3, 5, 8, 10], [1, 3, 6, 8, 10], [1, 4, 6, 8, 10], [1, 11], [1, 3, 11],
4815
+ [1, 4, 11], [1, 5, 11], [1, 6, 11], [1, 7, 11], [1, 8, 11], [1, 9, 11],
4816
+ [1, 3, 5, 11], [1, 3, 6, 11], [1, 3, 7, 11], [1, 3, 8, 11], [1, 3, 9, 11],
4817
+ [1, 4, 6, 11], [1, 4, 7, 11], [1, 4, 8, 11], [1, 4, 9, 11], [1, 5, 7, 11],
4818
+ [1, 5, 8, 11], [1, 5, 9, 11], [1, 6, 8, 11], [1, 6, 9, 11], [1, 7, 9, 11],
4819
+ [1, 3, 5, 7, 11], [1, 3, 5, 8, 11], [1, 3, 5, 9, 11], [1, 3, 6, 8, 11],
4820
+ [1, 3, 6, 9, 11], [1, 3, 7, 9, 11], [1, 4, 6, 8, 11], [1, 4, 6, 9, 11],
4821
+ [1, 4, 7, 9, 11], [1, 5, 7, 9, 11], [1, 3, 5, 7, 9, 11], [2], [2, 4], [2, 5],
4822
+ [2, 6], [2, 4, 6], [2, 7], [2, 4, 7], [2, 5, 7], [2, 8], [2, 4, 8], [2, 5, 8],
4823
+ [2, 6, 8], [2, 4, 6, 8], [2, 9], [2, 4, 9], [2, 5, 9], [2, 6, 9], [2, 7, 9],
4824
+ [2, 4, 6, 9], [2, 4, 7, 9], [2, 5, 7, 9], [2, 10], [2, 4, 10], [2, 5, 10],
4825
+ [2, 6, 10], [2, 7, 10], [2, 8, 10], [2, 4, 6, 10], [2, 4, 7, 10],
4826
+ [2, 4, 8, 10], [2, 5, 7, 10], [2, 5, 8, 10], [2, 6, 8, 10], [2, 4, 6, 8, 10],
4827
+ [2, 11], [2, 4, 11], [2, 5, 11], [2, 6, 11], [2, 7, 11], [2, 8, 11],
4828
+ [2, 9, 11], [2, 4, 6, 11], [2, 4, 7, 11], [2, 4, 8, 11], [2, 4, 9, 11],
4829
+ [2, 5, 7, 11], [2, 5, 8, 11], [2, 5, 9, 11], [2, 6, 8, 11], [2, 6, 9, 11],
4830
+ [2, 7, 9, 11], [2, 4, 6, 8, 11], [2, 4, 6, 9, 11], [2, 4, 7, 9, 11],
4831
+ [2, 5, 7, 9, 11], [3], [3, 5], [3, 6], [3, 7], [3, 5, 7], [3, 8], [3, 5, 8],
4832
+ [3, 6, 8], [3, 9], [3, 5, 9], [3, 6, 9], [3, 7, 9], [3, 5, 7, 9], [3, 10],
4833
+ [3, 5, 10], [3, 6, 10], [3, 7, 10], [3, 8, 10], [3, 5, 7, 10], [3, 5, 8, 10],
4834
+ [3, 6, 8, 10], [3, 11], [3, 5, 11], [3, 6, 11], [3, 7, 11], [3, 8, 11],
4835
+ [3, 9, 11], [3, 5, 7, 11], [3, 5, 8, 11], [3, 5, 9, 11], [3, 6, 8, 11],
4836
+ [3, 6, 9, 11], [3, 7, 9, 11], [3, 5, 7, 9, 11], [4], [4, 6], [4, 7], [4, 8],
4837
+ [4, 6, 8], [4, 9], [4, 6, 9], [4, 7, 9], [4, 10], [4, 6, 10], [4, 7, 10],
4838
+ [4, 8, 10], [4, 6, 8, 10], [4, 11], [4, 6, 11], [4, 7, 11], [4, 8, 11],
4839
+ [4, 9, 11], [4, 6, 8, 11], [4, 6, 9, 11], [4, 7, 9, 11], [5], [5, 7], [5, 8],
4840
+ [5, 9], [5, 7, 9], [5, 10], [5, 7, 10], [5, 8, 10], [5, 11], [5, 7, 11],
4841
+ [5, 8, 11], [5, 9, 11], [5, 7, 9, 11], [6], [6, 8], [6, 9], [6, 10],
4842
+ [6, 8, 10], [6, 11], [6, 8, 11], [6, 9, 11], [7], [7, 9], [7, 10], [7, 11],
4843
+ [7, 9, 11], [8], [8, 10], [8, 11], [9], [9, 11], [10], [11]]
4844
+
4845
+ ###################################################################################
4846
+
4847
+ MIDI_Instruments_Families = {
4848
+ 0: 'Piano Family',
4849
+ 1: 'Chromatic Percussion Family',
4850
+ 2: 'Organ Family',
4851
+ 3: 'Guitar Family',
4852
+ 4: 'Bass Family',
4853
+ 5: 'Strings Family',
4854
+ 6: 'Ensemble Family',
4855
+ 7: 'Brass Family',
4856
+ 8: 'Reed Family',
4857
+ 9: 'Pipe Family',
4858
+ 10: 'Synth Lead Family',
4859
+ 11: 'Synth Pad Family',
4860
+ 12: 'Synth Effects Family',
4861
+ 13: 'Ethnic Family',
4862
+ 14: 'Percussive Family',
4863
+ 15: 'Sound Effects Family',
4864
+ 16: 'Drums Family',
4865
+ -1: 'Unknown Family',
4866
+ }
4867
+
4868
+ ###################################################################################
4869
+
4870
+ def patch_to_instrument_family(MIDI_patch, drums_patch=128):
4871
+
4872
+ if 0 <= MIDI_patch < 128:
4873
+ return MIDI_patch // 8, MIDI_Instruments_Families[MIDI_patch // 8]
4874
+
4875
+ elif MIDI_patch == drums_patch:
4876
+ return MIDI_patch // 8, MIDI_Instruments_Families[16]
4877
+
4878
+ else:
4879
+ return -1, MIDI_Instruments_Families[-1]
4880
+
4881
+ ###################################################################################
4882
+
4883
+ def patch_list_from_enhanced_score_notes(enhanced_score_notes,
4884
+ default_patch=0,
4885
+ drums_patch=9,
4886
+ verbose=False
4887
+ ):
4888
+
4889
+ patches = [-1] * 16
4890
+
4891
+ for idx, e in enumerate(enhanced_score_notes):
4892
+ if e[3] != 9:
4893
+ if patches[e[3]] == -1:
4894
+ patches[e[3]] = e[6]
4895
+ else:
4896
+ if patches[e[3]] != e[6]:
4897
+ if e[6] in patches:
4898
+ e[3] = patches.index(e[6])
4899
+ else:
4900
+ if -1 in patches:
4901
+ patches[patches.index(-1)] = e[6]
4902
+ else:
4903
+ patches[-1] = e[6]
4904
+
4905
+ if verbose:
4906
+ print('=' * 70)
4907
+ print('WARNING! Composition has more than 15 patches!')
4908
+ print('Conflict note number:', idx)
4909
+ print('Conflict channel number:', e[3])
4910
+ print('Conflict patch number:', e[6])
4911
+
4912
+ patches = [p if p != -1 else default_patch for p in patches]
4913
+
4914
+ patches[9] = drums_patch
4915
+
4916
+ if verbose:
4917
+ print('=' * 70)
4918
+ print('Composition patches')
4919
+ print('=' * 70)
4920
+ for c, p in enumerate(patches):
4921
+ print('Cha', str(c).zfill(2), '---', str(p).zfill(3), Number2patch[p])
4922
+ print('=' * 70)
4923
+
4924
+ return patches
4925
+
4926
+ ###################################################################################
4927
+
4928
+ def patch_enhanced_score_notes(enhanced_score_notes,
4929
+ default_patch=0,
4930
+ drums_patch=9,
4931
+ verbose=False
4932
+ ):
4933
+
4934
+ #===========================================================================
4935
+
4936
+ enhanced_score_notes_with_patch_changes = []
4937
+
4938
+ patches = [-1] * 16
4939
+
4940
+ overflow_idx = -1
4941
+
4942
+ for idx, e in enumerate(enhanced_score_notes):
4943
+ if e[3] != 9:
4944
+ if patches[e[3]] == -1:
4945
+ patches[e[3]] = e[6]
4946
+ else:
4947
+ if patches[e[3]] != e[6]:
4948
+ if e[6] in patches:
4949
+ e[3] = patches.index(e[6])
4950
+ else:
4951
+ if -1 in patches:
4952
+ patches[patches.index(-1)] = e[6]
4953
+ else:
4954
+ overflow_idx = idx
4955
+ break
4956
+
4957
+ enhanced_score_notes_with_patch_changes.append(e)
4958
+
4959
+ #===========================================================================
4960
+
4961
+ overflow_patches = []
4962
+
4963
+ if overflow_idx != -1:
4964
+ for idx, e in enumerate(enhanced_score_notes[overflow_idx:]):
4965
+ if e[3] != 9:
4966
+ if e[6] not in patches:
4967
+ if e[6] not in overflow_patches:
4968
+ overflow_patches.append(e[6])
4969
+ enhanced_score_notes_with_patch_changes.append(['patch_change', e[1], e[3], e[6]])
4970
+ else:
4971
+ e[3] = patches.index(e[6])
4972
+
4973
+ enhanced_score_notes_with_patch_changes.append(e)
4974
+
4975
+ #===========================================================================
4976
+
4977
+ patches = [p if p != -1 else default_patch for p in patches]
4978
+
4979
+ patches[9] = drums_patch
4980
+
4981
+ #===========================================================================
4982
+
4983
+ if verbose:
4984
+ print('=' * 70)
4985
+ print('Composition patches')
4986
+ print('=' * 70)
4987
+ for c, p in enumerate(patches):
4988
+ print('Cha', str(c).zfill(2), '---', str(p).zfill(3), Number2patch[p])
4989
+ print('=' * 70)
4990
+
4991
+ if overflow_patches:
4992
+ print('Extra composition patches')
4993
+ print('=' * 70)
4994
+ for c, p in enumerate(overflow_patches):
4995
+ print(str(p).zfill(3), Number2patch[p])
4996
+ print('=' * 70)
4997
+
4998
+ return enhanced_score_notes_with_patch_changes, patches, overflow_patches
4999
+
5000
+ ###################################################################################
5001
+
5002
+ def create_enhanced_monophonic_melody(monophonic_melody):
5003
+
5004
+ enhanced_monophonic_melody = []
5005
+
5006
+ for i, note in enumerate(monophonic_melody[:-1]):
5007
+
5008
+ enhanced_monophonic_melody.append(note)
5009
+
5010
+ if note[1]+note[2] < monophonic_melody[i+1][1]:
5011
+
5012
+ delta_time = monophonic_melody[i+1][1] - (note[1]+note[2])
5013
+ enhanced_monophonic_melody.append(['silence', note[1]+note[2], delta_time, note[3], 0, 0, note[6]])
5014
+
5015
+ enhanced_monophonic_melody.append(monophonic_melody[-1])
5016
+
5017
+ return enhanced_monophonic_melody
5018
+
5019
+ ###################################################################################
5020
+
5021
+ def frame_monophonic_melody(monophonic_melody, min_frame_time_threshold=10):
5022
+
5023
+ mzip = list(zip(monophonic_melody[:-1], monophonic_melody[1:]))
5024
+
5025
+ times_counts = Counter([(b[1]-a[1]) for a, b in mzip]).most_common()
5026
+
5027
+ mc_time = next((item for item, count in times_counts if item >= min_frame_time_threshold), min_frame_time_threshold)
5028
+
5029
+ times = [(b[1]-a[1]) // mc_time for a, b in mzip] + [monophonic_melody[-1][2] // mc_time]
5030
+
5031
+ framed_melody = []
5032
+
5033
+ for i, note in enumerate(monophonic_melody):
5034
+
5035
+ stime = note[1]
5036
+ count = times[i]
5037
+
5038
+ if count != 0:
5039
+ for j in range(count):
5040
+
5041
+ new_note = copy.deepcopy(note)
5042
+ new_note[1] = stime + (j * mc_time)
5043
+ new_note[2] = mc_time
5044
+ framed_melody.append(new_note)
5045
+
5046
+ else:
5047
+ framed_melody.append(note)
5048
+
5049
+ return [framed_melody, mc_time]
5050
+
5051
+ ###################################################################################
5052
+
5053
+ def delta_score_notes(score_notes,
5054
+ timings_clip_value=255,
5055
+ even_timings=False,
5056
+ compress_timings=False
5057
+ ):
5058
+
5059
+ delta_score = []
5060
+
5061
+ pe = score_notes[0]
5062
+
5063
+ for n in score_notes:
5064
+
5065
+ note = copy.deepcopy(n)
5066
+
5067
+ time = n[1] - pe[1]
5068
+ dur = n[2]
5069
+
5070
+ if even_timings:
5071
+ if time != 0 and time % 2 != 0:
5072
+ time += 1
5073
+ if dur % 2 != 0:
5074
+ dur += 1
5075
+
5076
+ time = max(0, min(timings_clip_value, time))
5077
+ dur = max(0, min(timings_clip_value, dur))
5078
+
5079
+ if compress_timings:
5080
+ time /= 2
5081
+ dur /= 2
5082
+
5083
+ note[1] = int(time)
5084
+ note[2] = int(dur)
5085
+
5086
+ delta_score.append(note)
5087
+
5088
+ pe = n
5089
+
5090
+ return delta_score
5091
+
5092
+ ###################################################################################
5093
+
5094
+ # This is the end of the TMIDI X Python module
5095
+
5096
+ ###################################################################################
midi_to_colab_audio.py ADDED
The diff for this file is too large to render. See raw diff