#======================================================================= # https://huggingface.co/spaces/asigalov61/Guided-Rock-Music-Transformer #======================================================================= import os import time as reqtime import datetime from pytz import timezone import copy from itertools import groupby import tqdm import spaces import gradio as gr import torch from x_transformer_1_23_2 import * import random import TMIDIX from midi_to_colab_audio import midi_to_colab_audio # ================================================================================================= @spaces.GPU def Generate_Rock_Song(input_midi, input_gen_type, input_number_prime_chords, input_number_gen_chords, input_use_original_durations, input_match_original_pitches_counts, input_number_prime_tokens, input_number_gen_tokens, input_num_memory_tokens, input_model_temperature, input_model_top_p ): #=============================================================================== print('=' * 70) print('Req start time: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now(PDT))) start_time = reqtime.time() print('=' * 70) fn = os.path.basename(input_midi) fn1 = fn.split('.')[0] print('=' * 70) print('Requested settings:') print('=' * 70) print('Input MIDI file name:', fn) print('Generation type:', input_gen_type) print('Number of prime chords:', input_number_prime_chords) print('Number of chords to generate:', input_number_gen_chords) print('Use original durations:', input_use_original_durations) print('Match original pitches counts:', input_match_original_pitches_counts) print('Number of prime tokens:', input_number_prime_tokens) print('Number of tokens to generate:', input_number_gen_tokens) print('Number of memory tokens:', input_num_memory_tokens) print('Model temperature:', input_model_temperature) print('Model sampling top k value:', input_model_top_k) print('=' * 70) #=============================================================================== print('Loading model...') SEQ_LEN = 4096 PAD_IDX = 673 DEVICE = 'cuda' # 'cpu' # instantiate the model model = TransformerWrapper( num_tokens = PAD_IDX+1, max_seq_len = SEQ_LEN, attn_layers = Decoder(dim = 1024, depth = 16, heads = 16, rotary_pos_emb=True, attn_flash = True) ) model = AutoregressiveWrapper(model, ignore_index = PAD_IDX) model.to(DEVICE) print('=' * 70) print('Loading model checkpoint...') model.load_state_dict( torch.load('Guided_Rpck_Music_Transformer_Trained_Model_12081_steps_0.4113_loss_0.8747_acc.pth', map_location=DEVICE)) print('=' * 70) model.eval() if DEVICE == 'cpu': dtype = torch.bfloat16 else: dtype = torch.bfloat16 ctx = torch.amp.autocast(device_type=DEVICE, dtype=dtype) print('Done!') print('=' * 70) #=============================================================================== print('Loading MIDI...') #=============================================================================== # Raw single-track ms score raw_score = TMIDIX.midi2single_track_ms_score(input_midi.name) #=============================================================================== # Enhanced score notes escore_notes = TMIDIX.advanced_score_processor(raw_score, return_enhanced_score_notes=True)[0] escore_notes = [e for e in escore_notes if e[6] < 72 or e[6] == 128] #======================================================= # PRE-PROCESSING #=============================================================================== # Augmented enhanced score notes escore_notes = TMIDIX.augment_enhanced_score_notes(escore_notes, timings_divider=32, legacy_timings=True) #=============================================================================== dscore = TMIDIX.enhanced_delta_score_notes(escore_notes) cscore = TMIDIX.chordify_score(dscore) #=============================================================================== score_toks = [] control_toks = [] prime_toks = [] for c in cscore: ctime = c[0][0] #================================================================= chord = sorted(c, key=lambda x: -x[5]) gnotes = [] gdrums = [] for k, v in groupby(chord, key=lambda x: x[5]): if k == 128: gdrums.extend(sorted(v, key=lambda x: x[3], reverse=True)) else: gnotes.append(sorted(v, key=lambda x: x[3], reverse=True)) #================================================================= chord_toks = [] ctoks = [] ptoks = [] chord_toks.append(ctime) ptoks.append(ctime) if gdrums: chord_toks.extend([e[3]+128 for e in gdrums] + [128]) ptoks.extend([e[3]+128 for e in gdrums] + [128]) else: chord_toks.append(128) ptoks.append(128) if gnotes: for g in gnotes: durs = [e[1] // 4 for e in g] clipped_dur = max(1, min(31, min(durs))) chan = max(0, min(8, g[0][5] // 8)) chan_dur_tok = ((chan * 32) + clipped_dur) + 256 ctoks.append([chan_dur_tok, len(g)]) ptoks.append(chan_dur_tok) ptoks.extend([e[3]+544 for e in g]) score_toks.append(chord_toks) control_toks.append(ctoks) prime_toks.append(ptoks) print('Done!') print('=' * 70) #================================================================== print('Sample output events', prime_toks[:16]) print('=' * 70) print('Generating...') #================================================================== def generate_continuation(num_prime_tokens, num_gen_tokens): x = torch.tensor(TMIDIX.flatten(prime_toks)[:num_prime_tokens], dtype=torch.long, device=DEVICE) with ctx: out = model.generate(x, num_gen_tokens, filter_logits_fn=top_p, filter_kwargs={'thres': input_model_top_p}, temperature=input_model_temperature, return_prime=True, verbose=True) y = out.tolist()[0] return y #================================================================== def generate_tokens(seq, max_num_ptcs=5, max_tries=10): input = copy.deepcopy(seq) pcount = 0 y = 545 tries = 0 gen_tokens = [] seen = False if 256 < input[-1] < 544: seen = True while pcount < max_num_ptcs and y > 255 and tries < max_tries: x = torch.tensor(input[-input_num_memory_tokens:], dtype=torch.long, device=DEVICE) with ctx: out = model.generate(x, 1, filter_logits_fn=top_k, filter_logits_fn=top_p, filter_kwargs={'thres': input_model_top_p}, return_prime=False, verbose=False) y = out[0].tolist()[0] if 256 < y < 544: if not seen: input.append(y) gen_tokens.append(y) seen = True else: tries += 1 if y > 544 and seen: if pcount < max_num_ptcs and y not in gen_tokens: input.append(y) gen_tokens.append(y) pcount += 1 else: tries += 1 return gen_tokens #================================================================== song = [] if input_gen_type == 'Freestyle': output = generate_continuation(input_number_prime_tokens, input_number_gen_tokens) song.extend(output) else: for i in range(input_number_prime_chords): song.extend(prime_toks[i]) for i in tqdm.tqdm(range(input_number_prime_chords, input_number_prime_chords+input_number_gen_chords)): song.extend(score_toks[i]) if control_toks[i]: for ct in control_toks[i]: if input_use_original_durations: song.append(ct[0]) if input_match_original_pitches_counts: out_seq = generate_tokens(song, ct[1]) else: out_seq = generate_tokens(song) song.extend(out_seq) print('=' * 70) print('Done!') print('=' * 70) #=============================================================================== print('Rendering results...') print('=' * 70) print('Sample INTs', song[:15]) print('=' * 70) if len(song) != 0: song_f = [] time = 0 dur = 32 channel = 0 pitch = 60 vel = 90 patches = [0, 10, 19, 24, 35, 40, 52, 56, 65, 9, 0, 0, 0, 0, 0, 0] velocities = [80, 100, 90, 100, 110, 100, 100, 100, 100, 110] for ss in song: if 0 <= ss < 128: time += ss * 32 if 128 < ss < 256: song_f.append(['note', time, 32, 9, ss-128, velocities[9], 128]) if 256 < ss < 544: dur = ((ss-256) % 32) * 4 * 32 channel = (ss-256) // 32 if 544 < ss < 672: patch = channel * 8 pitch = ss-544 song_f.append(['note', time, dur, channel, pitch, velocities[channel], patch]) fn1 = "Guided-Rock-Music-Transformer-Composition" detailed_stats = TMIDIX.Tegridy_ms_SONG_to_MIDI_Converter(song_f, output_signature = 'Guided Rock Music Transformer', output_file_name = fn1, track_name='Project Los Angeles', list_of_MIDI_patches=patches ) new_fn = fn1+'.mid' audio = midi_to_colab_audio(new_fn, soundfont_path=soundfont, sample_rate=16000, volume_scale=10, output_for_gradio=True ) print('Done!') print('=' * 70) #======================================================== output_midi_title = str(fn1) output_midi_summary = str(song_f[:3]) output_midi = str(new_fn) output_audio = (16000, audio) output_plot = TMIDIX.plot_ms_SONG(song_f, plot_title=output_midi, return_plt=True) print('Output MIDI file name:', output_midi) print('Output MIDI title:', output_midi_title) print('Output MIDI summary:', output_midi_summary) print('=' * 70) #======================================================== print('-' * 70) print('Req end time: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now(PDT))) print('-' * 70) print('Req execution time:', (reqtime.time() - start_time), 'sec') return output_midi_title, output_midi_summary, output_midi, output_audio, output_plot # ================================================================================================= if __name__ == "__main__": PDT = timezone('US/Pacific') print('=' * 70) print('App start time: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now(PDT))) print('=' * 70) soundfont = "SGM-v2.01-YamahaGrand-Guit-Bass-v2.7.sf2" app = gr.Blocks() with app: gr.Markdown("