Spaces:
Running
on
Zero
Running
on
Zero
#======================================================================= | |
# https://huggingface.co/spaces/asigalov61/Guided-Rock-Music-Transformer | |
#======================================================================= | |
import os | |
import time as reqtime | |
import datetime | |
from pytz import timezone | |
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 | |
# ================================================================================================= | |
def Generate_Rock_Song(input_midi, | |
input_freestyle_continuation, | |
input_number_prime_chords, | |
input_use_original_durations, | |
input_match_original_pitches_counts | |
): | |
#=============================================================================== | |
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('Freestyle continuation:', input_freestyle_continuation) | |
print('Number of prime chords:', input_number_prime_chords) | |
print('Use original durations:', input_use_original_durations) | |
print('Match original pitches counts:', input_match_original_pitches_counts) | |
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('Melody2Song_Seq2Seq_Music_Transformer_Trained_Model_28482_steps_0.719_loss_0.7865_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) | |
#=============================================================================== | |
# 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(prime_toks[:num_prime_tokens], dtype=torch.long, device=DEVICE) | |
with ctx: | |
out = model.generate(x, | |
num_gen_tokens, | |
#filter_logits_fn=top_k, | |
#filter_kwargs={'k': 5}, | |
temperature=0.9, | |
return_prime=True, | |
verbose=True) | |
y = out.tolist()[0] | |
return y | |
#================================================================== | |
def generate_tokens(seq, max_num_ptcs=10): | |
input = copy.deepcopy(seq) | |
pcount = 0 | |
y = 545 | |
gen_tokens = [] | |
while pcount < max_num_ptcs and y > 255: | |
x = torch.tensor(input, dtype=torch.long, device=DEVICE) | |
with ctx: | |
out = model.generate(x, | |
1, | |
filter_logits_fn=top_k, | |
filter_kwargs={'k': 10}, | |
temperature=0.9, | |
return_prime=False, | |
verbose=False) | |
y = out[0].tolist()[0] | |
if pcount < max_num_ptcs and y > 255: | |
input.append(y) | |
gen_tokens.append(y) | |
if y > 544: | |
pcount += 1 | |
return gen_tokens | |
#================================================================== | |
song = [] | |
if input_freestyle_continuation: | |
output = generate_continuation(512, 1024) | |
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, len(score_toks))): | |
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', output[:15]) | |
print('=' * 70) | |
out1 = output | |
if len(out1) != 0: | |
song = out1 | |
song_f = [] | |
time = 0 | |
dur = 32 | |
channel = 0 | |
pitch = 60 | |
vel = 90 | |
patches = [0, 10, 19, 24, 35, 40, 52, 56, 65, 9, 73, 46, 0, 0, 0, 0] | |
for ss in song: | |
if 0 <= ss < 128: | |
time += ss * 32 | |
if 128 < ss < 256: | |
song_f.append(['note', time, 32, 9, ss-128, 110, 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, vel, patch]) | |
song_f, patches, overflow_patches = TMIDIX.patch_enhanced_score_notes(song_f) | |
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("<h1 style='text-align: center; margin-bottom: 1rem'>Guided Rock Music Transformer</h1>") | |
gr.Markdown("<h1 style='text-align: center; margin-bottom: 1rem'>Generate unique rock music compositions with source augmented RoPE music transformer</h1>") | |
gr.Markdown( | |
"![Visitors](https://api.visitorbadge.io/api/visitors?path=asigalov61.Guided-Rock-Music-Transformer&style=flat)\n\n") | |
gr.Markdown("## Upload your MIDI or select a sample example MIDI below") | |
input_midi = gr.File(label="Input MIDI", file_types=[".midi", ".mid", ".kar"]) | |
gr.Markdown("## Select generation options") | |
input_freestyle_continuation = gr.Checkbox(label="Freestyle continuation", value=False) | |
input_number_prime_chords = gr.Slider(0, 512, value=128, step=8, label="Number of prime chords") | |
input_use_original_durations = gr.Checkbox(label="Use original durations", value=False) | |
input_match_original_pitches_counts = gr.Checkbox(label="Match original pitches counts", value=False) | |
run_btn = gr.Button("generate", variant="primary") | |
gr.Markdown("## Generation results") | |
output_midi_title = gr.Textbox(label="Output MIDI title") | |
output_midi_summary = gr.Textbox(label="Output MIDI summary") | |
output_audio = gr.Audio(label="Output MIDI audio", format="wav", elem_id="midi_audio") | |
output_plot = gr.Plot(label="Output MIDI score plot") | |
output_midi = gr.File(label="Output MIDI file", file_types=[".mid"]) | |
run_event = run_btn.click(Generate_Rock_Song, [input_freestyle_continuation, | |
input_number_prime_chords, | |
input_use_original_durations, | |
input_match_original_pitches_counts | |
], | |
[output_midi_title, output_midi_summary, output_midi, output_audio, output_plot]) | |
gr.Examples( | |
[["Sharing The Night Together.kar", 0, True], | |
], | |
[input_midi, | |
input_melody_seed_number, | |
input_find_best_match, | |
], | |
[output_midi_title, output_midi_summary, output_midi, output_audio, output_plot], | |
Generate_Rock_Song, | |
cache_examples=False, | |
) | |
app.queue().launch() |