projectlosangeles commited on
Commit
7b620a5
·
verified ·
1 Parent(s): ddef700

Upload 6 files

Browse files
Files changed (6) hide show
  1. TMIDIX.py +0 -0
  2. app.py +477 -0
  3. midi_to_colab_audio.py +0 -0
  4. packages.txt +1 -0
  5. requirements.txt +10 -0
  6. x_transformer_2_3_1.py +0 -0
TMIDIX.py ADDED
The diff for this file is too large to render. See raw diff
 
app.py ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #=================================================================================
2
+ # https://huggingface.co/spaces/projectlosangeles/Orpheus-Masked-Pitches-Inpainter
3
+ #=================================================================================
4
+
5
+ print('=' * 70)
6
+ print('Orpheus Masked Pitches Inpainter Gradio App')
7
+ print('=' * 70)
8
+
9
+ import os
10
+
11
+ os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
12
+ os.environ['USE_FLASH_ATTENTION'] = '1'
13
+
14
+ import time as reqtime
15
+ from pytz import timezone
16
+
17
+ import torch
18
+
19
+ torch.set_float32_matmul_precision('high')
20
+ torch.backends.cuda.matmul.allow_tf32 = True
21
+ torch.backends.cudnn.allow_tf32 = True
22
+ torch.backends.cuda.enable_mem_efficient_sdp(True)
23
+ torch.backends.cuda.enable_math_sdp(True)
24
+ torch.backends.cuda.enable_flash_sdp(True)
25
+ torch.backends.cuda.enable_cudnn_sdp(True)
26
+
27
+ import spaces
28
+ import gradio as gr
29
+
30
+ from x_transformer_2_3_1 import *
31
+
32
+ import datetime
33
+ import random
34
+ import tqdm
35
+
36
+ from midi_to_colab_audio import midi_to_colab_audio
37
+ import TMIDIX
38
+
39
+ import matplotlib.pyplot as plt
40
+
41
+ from huggingface_hub import hf_hub_download
42
+
43
+ # =================================================================================================
44
+
45
+ OUTPUT_MIDIS_DIR = 'output_midis'
46
+
47
+ # =================================================================================================
48
+
49
+ print('=' * 70)
50
+ print('Loading models...')
51
+ print('=' * 70)
52
+ print('Loading Orpheus masked encoder model...')
53
+ print('=' * 70)
54
+
55
+ SEQ_LEN = 2048
56
+ PAD_IDX = 18820
57
+ DEVICE = 'cuda'
58
+
59
+ model = TransformerWrapper(
60
+ num_tokens = PAD_IDX+1,
61
+ max_seq_len = SEQ_LEN,
62
+ attn_layers = Encoder(dim = 2048,
63
+ depth = 12,
64
+ heads = 16,
65
+ rotary_pos_emb = True,
66
+ attn_flash = True
67
+ )
68
+ )
69
+
70
+ model.to(DEVICE)
71
+
72
+ print('=' * 70)
73
+ print('Loading model checkpoint...')
74
+
75
+ checkpoint = hf_hub_download(
76
+ repo_id='asigalov61/Orpheus-Music-Transformer',
77
+ filename='Orpheus_Music_Transformer_Masked_Encoder_Trained_Model_23000_steps_0.6548_loss_0.8132_acc.pth'
78
+ )
79
+
80
+ model.load_state_dict(torch.load(checkpoint, map_location=DEVICE, weights_only=True))
81
+
82
+ model.eval()
83
+
84
+ # model = torch.compile(model)
85
+
86
+ print('=' * 70)
87
+ print('Done!')
88
+ print('=' * 70)
89
+
90
+ # =================================================================================================
91
+
92
+ dtype = torch.bfloat16
93
+
94
+ ctx = torch.amp.autocast(device_type=DEVICE, dtype=dtype)
95
+
96
+ print('Done!')
97
+ print('=' * 70)
98
+
99
+ # =================================================================================================
100
+
101
+ print('Loading SoundFont...')
102
+
103
+ SOUNDFONT_PATH = hf_hub_download(repo_id='projectlosangeles/soundfonts4u',
104
+ repo_type='dataset',
105
+ filename='SGM-v2.01-YamahaGrand-Guit-Bass-v2.7.sf2'
106
+ )
107
+
108
+ print('Done!')
109
+ print('=' * 70)
110
+
111
+ # =================================================================================================
112
+
113
+ def load_midi(input_midi):
114
+
115
+ """Process the input MIDI file and create a token sequence."""
116
+
117
+ raw_score = TMIDIX.midi2single_track_ms_score(input_midi.name, do_not_check_MIDI_signature=True)
118
+
119
+ escore_notes = TMIDIX.advanced_score_processor(raw_score,
120
+ return_enhanced_score_notes=True,
121
+ apply_sustain=True
122
+ )
123
+
124
+ if escore_notes and escore_notes[0]:
125
+
126
+ escore_notes = TMIDIX.augment_enhanced_score_notes(escore_notes[0],
127
+ sort_drums_last=True
128
+ )
129
+
130
+ escore_notes = TMIDIX.remove_duplicate_pitches_from_escore_notes(escore_notes)
131
+
132
+ escore_notes = TMIDIX.fix_escore_notes_durations(escore_notes,
133
+ min_notes_gap=0
134
+ )
135
+
136
+ dscore = TMIDIX.delta_score_notes(escore_notes)
137
+
138
+ dcscore = TMIDIX.chordify_score([d[1:] for d in dscore])
139
+
140
+ melody_chords = [18816]
141
+
142
+ #=======================================================
143
+ # MAIN PROCESSING CYCLE
144
+ #=======================================================
145
+
146
+ for i, c in enumerate(dcscore):
147
+
148
+ delta_time = c[0][0]
149
+
150
+ melody_chords.append(delta_time)
151
+
152
+ for e in c:
153
+
154
+ #=======================================================
155
+
156
+ # Durations
157
+ dur = max(1, min(255, e[1]))
158
+
159
+ # Patches
160
+ pat = max(0, min(128, e[5]))
161
+
162
+ # Pitches
163
+ ptc = max(1, min(127, e[3]))
164
+
165
+ # Velocities
166
+ # Calculating octo-velocity
167
+ vel = max(8, min(127, e[4]))
168
+ velocity = round(vel / 15)-1
169
+
170
+ #=======================================================
171
+ # FINAL NOTE SEQ
172
+ #=======================================================
173
+
174
+ # Writing final note
175
+ pat_ptc = (128 * pat) + ptc
176
+ dur_vel = (8 * dur) + velocity
177
+
178
+ melody_chords.extend([pat_ptc+256, dur_vel+16768])
179
+
180
+ return melody_chords
181
+
182
+ else:
183
+ return [18816]
184
+
185
+ # =================================================================================================
186
+
187
+ def save_midi(tokens):
188
+
189
+ """Convert token sequence back to a MIDI score and write it using TMIDIX.
190
+ """
191
+
192
+ time = 0
193
+ dur = 1
194
+ vel = 90
195
+ pitch = 60
196
+ channel = 0
197
+ patch = 0
198
+
199
+ patches = [-1] * 16
200
+
201
+ channels = [0] * 16
202
+ channels[9] = 1
203
+
204
+ song_f = []
205
+
206
+ for ss in tokens:
207
+
208
+ if 0 <= ss < 256:
209
+
210
+ time += ss * 16
211
+
212
+ if 256 <= ss < 16768:
213
+
214
+ patch = (ss-256) // 128
215
+
216
+ if patch < 128:
217
+
218
+ if patch not in patches:
219
+ if 0 in channels:
220
+ cha = channels.index(0)
221
+ channels[cha] = 1
222
+ else:
223
+ cha = 15
224
+
225
+ patches[cha] = patch
226
+ channel = patches.index(patch)
227
+ else:
228
+ channel = patches.index(patch)
229
+
230
+ if patch == 128:
231
+ channel = 9
232
+
233
+ pitch = (ss-256) % 128
234
+
235
+
236
+ if 16768 <= ss < 18816:
237
+
238
+ dur = ((ss-16768) // 8) * 16
239
+ vel = (((ss-16768) % 8)+1) * 15
240
+
241
+ song_f.append(['note', time, dur, channel, pitch, vel, patch])
242
+
243
+ if song_f is not None and song_f:
244
+
245
+ song_f = TMIDIX.remove_duplicate_pitches_from_escore_notes(song_f)
246
+
247
+ song_f = TMIDIX.fix_escore_notes_durations(song_f,
248
+ min_notes_gap=0
249
+ )
250
+
251
+ output_score, patches, overflow_patches = TMIDIX.patch_enhanced_score_notes(song_f)
252
+
253
+ now = datetime.datetime.now(PDT)
254
+ ms4 = now.strftime("%f")[:4] # first four digits of microseconds
255
+
256
+ fname = (
257
+ "Orpheus-Masked-Pitches-Inpainter-Composition-"
258
+ + now.strftime(f"%Y-%m-%d-%H-%M-%S-{ms4}")
259
+ )
260
+
261
+ os.makedirs(OUTPUT_MIDIS_DIR, exist_ok=True)
262
+
263
+ output_fname = os.path.join(OUTPUT_MIDIS_DIR, fname)
264
+
265
+ TMIDIX.Tegridy_ms_SONG_to_MIDI_Converter(
266
+ output_score,
267
+ output_signature='Orpheus Masked Pitches Inpainter',
268
+ output_file_name=output_fname,
269
+ track_name='Project Los Angeles',
270
+ list_of_MIDI_patches=patches,
271
+ verbose=False
272
+ )
273
+ return output_fname, output_score
274
+
275
+ else:
276
+ return None, None
277
+
278
+ # =================================================================================================
279
+
280
+ @spaces.GPU
281
+ def inpaint_pitches(inp_seq,
282
+ input_patch,
283
+ input_inpaint_ratio,
284
+ input_num_prime_notes
285
+ ):
286
+
287
+ print('*' * 70)
288
+ print('Inpainting pitches...')
289
+
290
+ inp_seq = inp_seq[:SEQ_LEN]
291
+
292
+ m_pos = [i for i in range(SEQ_LEN) if (128*input_patch)+256 < inp_seq[i] < (128*(input_patch+1))+256]
293
+
294
+ m_pos = m_pos[min(len(m_pos), input_num_prime_notes):]
295
+
296
+ if input_inpaint_ratio < 1:
297
+ m_pos = sorted(random.sample(m_pos, k=int(round(len(m_pos) * input_inpaint_ratio))))
298
+
299
+ results = predict_masked_tokens(model, inp_seq, mask_positions=m_pos, topk=1)
300
+
301
+ output_seq = results['predicted_ids']
302
+
303
+ print('Done!')
304
+ print('=' * 70)
305
+
306
+ return output_seq
307
+
308
+ # =================================================================================================
309
+
310
+ def Inpaint_Pitches(input_midi,
311
+ input_patch,
312
+ input_inpaint_ratio,
313
+ input_num_prime_notes
314
+ ):
315
+
316
+ if input_midi is not None:
317
+
318
+ print('=' * 70)
319
+ print('Req start time: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now(PDT)))
320
+ start_time = reqtime.time()
321
+
322
+ print('=' * 70)
323
+ fn = os.path.basename(input_midi.name)
324
+ fn1 = fn.split('.')[0]
325
+ print('Input file name:', fn)
326
+ print('Input patch:', input_patch)
327
+ print('Input inpaint ratio:', input_inpaint_ratio)
328
+ print('Input number of prime notes:', input_num_prime_notes)
329
+ print('=' * 70)
330
+ print('Loading MIDI...')
331
+ inp_seq = load_midi(input_midi)
332
+
333
+ print('Composition has', len(inp_seq), 'tokens')
334
+ print('Sample composition tokens:', inp_seq[:5])
335
+ print('=' * 70)
336
+
337
+ #===============================================================================
338
+
339
+ output_seq = inpaint_pitches(inp_seq,
340
+ input_patch,
341
+ input_inpaint_ratio,
342
+ input_num_prime_notes
343
+ )
344
+
345
+ #===============================================================================
346
+
347
+ print('Saving MIDI...')
348
+ print('=' * 70)
349
+
350
+ output_fname, output_score = save_midi(output_seq)
351
+
352
+ #===============================================================================
353
+
354
+ print('Rendering results...')
355
+ print('=' * 70)
356
+
357
+ audio = midi_to_colab_audio(output_fname+'.mid',
358
+ soundfont_path=SOUNDFONT_PATH,
359
+ sample_rate=16000,
360
+ output_for_gradio=True
361
+ )
362
+
363
+ #========================================================
364
+
365
+ output_audio = (16000, audio)
366
+
367
+ output_plot = TMIDIX.plot_ms_SONG(output_score,
368
+ plot_title=os.path.basename(output_fname)+'.mid',
369
+ return_plt=True
370
+ )
371
+
372
+ print('Done!')
373
+ print('=' * 70)
374
+
375
+ #========================================================
376
+
377
+ print('-' * 70)
378
+ print('Req end time: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now(PDT)))
379
+ print('-' * 70)
380
+ print('Req execution time:', (reqtime.time() - start_time), 'sec')
381
+
382
+ return output_audio, output_plot, output_fname+'.mid'
383
+
384
+ return None, None, None
385
+
386
+ # =================================================================================================
387
+
388
+ PDT = timezone('US/Pacific')
389
+
390
+ print('=' * 70)
391
+ print('App start time: {:%Y-%m-%d %H:%M:%S}'.format(datetime.datetime.now(PDT)))
392
+ print('=' * 70)
393
+
394
+ soundfont = "SGM-v2.01-YamahaGrand-Guit-Bass-v2.7.sf2"
395
+
396
+ app = gr.Blocks()
397
+ with app:
398
+ gr.Markdown("<h1 style='text-align: left; margin-bottom: 1rem'>Orpheus Masked Pitches Inpainter</h1>")
399
+ gr.Markdown("<h1 style='text-align: left; margin-bottom: 1rem'>Instantly inpaint pitches in any MIDI with Orpheus masked encoder</h1>")
400
+
401
+ with gr.Row(elem_classes="duplicate-row"):
402
+ gr.DuplicateButton(
403
+ value="🤗 Duplicate 🤗",
404
+ variant="huggingface",
405
+ size="md",
406
+ link="https://huggingface.co/spaces/projectlosangeles/Orpheus-Masked-Pitches-Inpainter?duplicate=true",
407
+ link_target="_blank"
408
+ )
409
+
410
+ gr.Button(
411
+ value="❤️ Models ❤️",
412
+ variant="huggingface",
413
+ size="md",
414
+ link="https://huggingface.co/asigalov61/Orpheus-Music-Transformer",
415
+ link_target="_blank"
416
+ )
417
+
418
+ gr.Button(
419
+ value="🚀 Spaces 🚀",
420
+ variant="huggingface",
421
+ size="md",
422
+ link="https://huggingface.co/collections/asigalov61/orpheus-music-transformer",
423
+ link_target="_blank"
424
+ )
425
+
426
+ gr.Button(
427
+ value="🦖 Dataset 🦖",
428
+ variant="huggingface",
429
+ size="md",
430
+ link="https://huggingface.co/datasets/projectlosangeles/Godzilla-MIDI-Dataset",
431
+ link_target="_blank"
432
+ )
433
+
434
+ gr.Markdown("## Upload your MIDI or select an example MIDI at the bottom of the page")
435
+
436
+ input_midi = gr.File(label="Input MIDI", file_types=["midi", ".mid", ".kar"])
437
+
438
+ input_patch = gr.Slider(0, 128, value=40, step=1, label="Patch number to inpaint")
439
+ input_num_prime_notes = gr.Slider(0, 64, value=16, step=1, label="Number of prime notes")
440
+ input_inpaint_ratio = gr.Slider(0.01, 1.0, value=1, step=0.01, label="Pitches inpaint ratio")
441
+
442
+ run_btn = gr.Button("Inpaint Pitches", variant="primary")
443
+
444
+ gr.Markdown("## Generation results")
445
+
446
+ output_audio = gr.Audio(label="Output MIDI audio", format="mp3", elem_id="midi_audio")
447
+ output_plot = gr.Plot(label="Output MIDI score plot")
448
+ output_midi = gr.File(label="Output MIDI file", file_types=[".mid"])
449
+
450
+ run_event = run_btn.click(Inpaint_Pitches,
451
+ [input_midi,
452
+ input_patch,
453
+ input_inpaint_ratio,
454
+ input_num_prime_notes
455
+ ],
456
+ [output_audio,
457
+ output_plot,
458
+ output_midi
459
+ ])
460
+
461
+ gr.Examples(
462
+ [["Gang Stop.mid", 40, 1, 16],
463
+ ["Soli.mid", 40, 1, 16]
464
+ ],
465
+ [input_midi,
466
+ input_patch,
467
+ input_inpaint_ratio,
468
+ input_num_prime_notes
469
+ ],
470
+ [output_audio,
471
+ output_plot,
472
+ output_midi
473
+ ],
474
+ Inpaint_Pitches
475
+ )
476
+
477
+ app.launch(mcp_server=True)
midi_to_colab_audio.py ADDED
The diff for this file is too large to render. See raw diff
 
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ fluidsynth
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ tqdm
2
+ numpy
3
+ scikit-learn
4
+ matplotlib
5
+ gradio
6
+ hf-transfer
7
+ huggingface_hub
8
+ torch
9
+ einops
10
+ einx
x_transformer_2_3_1.py ADDED
The diff for this file is too large to render. See raw diff